Lock-线程组-线程池-线程状态的转换图

一.Lock(接口)

1.使用同步机制的方式解决线程安全问题,但是不知道具体的锁对象在哪里添加,并且锁对象在哪里释放锁对象,对于这种情况,jdk5以后java提供了一个更具体的锁对象

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作

Lock是一个接口,所以它在使用时用 ReentrantLock子实现类

2.加锁和释放锁的方法:

public void lock()获取锁

public void unlock()试图释放此锁

try{

锁对象.lock()

..........

}finally{

锁对象.unlock() //释放资源

}

package lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTickets implements Runnable{
	private static int tickets = 100;
	private Lock lock = new ReentrantLock();
	@Override
	public void run() {
		// 模拟电影院售票,假设一直有票
		while (true){
			//加锁
			try{
				lock.lock();
				
				if(tickets>0){
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets--+"张票");
				}			
			//释放锁	
			}finally{
				lock.unlock();
			}			
		}
	}
}
package lock;

public class SellTicketsDemo {

	public static void main(String[] args) {
		// 创建SynRunnable对象
		SellTickets st = new SellTickets();
		
		//创建线程类对象
		Thread t1 = new Thread(st,"窗口1");
		Thread t2 = new Thread(st,"窗口2");
		Thread t3 = new Thread(st,"窗口3");
		
		//开启线程
		t1.start();
		t2.start();
		t3.start();
	}
}
3.同步机制的优缺点
优点:解决多线程的安全问题

缺点:1)同步,执行效率低(每个线程在抢占到CPU的执行权后,会将门关闭,其他线程不能进来)

  2)容易出现死锁:两个或者两个以上的线程出现了互相等待的情况,容易死锁
4.线程间的通信:生产者消费者模式(多个线程对同一资源进行操作)

例子: 1)资源对象:Student类   提供一些成员变量:姓名 和年龄

2)生产者线程:SetThread类:  生产一些学生数据(设置学生数据)

    3)消费者线程:GetThread类:  输出学生数据

   4)测试类:StudentDemo类,实现多线程环境

对于生产者来说,产生数据,如果处于等待(wait)状态就说名有数据,通知消费者有线程,赶紧输出数据

对于消费者线程,输出数据,如果没有数据,应该通知生产者产生数据。

    以上是一个多线程环境,会出现线程不安全

1)同一数据打印一片(CPU的一点点时间片足够执行很多此)

2)年龄和姓名不符合(线程的随机性导致的)

使用等待唤醒机制可以解决问题1);使用同步机制可以解决问题2)

package waitnotify;

public class Student {
	String name;
	int age;
	
	//声明一个变量
	boolean flag;//默认没有数据,如果是true,则说明有数据
}
package waitnotify;
//生产者线程
public class SetThread implements Runnable{

	private Student s;
	public SetThread(Student s){
		this.s = s;
	}
	private int i = 0;
	@Override
	public void run() {
		while(true){
			//同步机制可以解决数据不一致
			synchronized(s){
				//等待唤醒机制
				//判断有没有数据
				if(s.flag){  //对于生产者来说,没有数据,则处于等待状态,
					//处于等待状态
					try {
						s.wait();//阻塞方式
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				if(i%2==0){
					//设置学生数据
					s.name = "韩庚";
					s.age = 18;
				}else{
					//设置学生数据
					s.name = "张靓颖";
					s.age = 16;
				}
				i++;
				
				//修改标记
				s.flag = true;//有数据
				//通知gt:消费者线程来消费
				s.notify();//唤醒等待状态
			}
			
		}
		
	}

}
package waitnotify;

public class GetThread implements Runnable{
	private Student s;
	public GetThread(Student s){
		this.s = s;
	}
	
	@Override
	public void run() {
		//使用同步机制可以解决数据不一致现象
		while(true){
			synchronized(s){
				
				//使用等待唤醒机制
				if(!s.flag){
					try {
						s.wait();//调用的时候会立即释放锁
					} catch (InterruptedException e) {
		
						e.printStackTrace();
					}
				}
				
			//输出语句
			System.out.println(s.name+"----"+s.age);
			
			//修改标记
			s.flag = false;//消费者线程没有可输出的数据
			
			//通知对方st线程,消费者线程没有数据,赶紧产生数据
			s.notify();		//唤醒st线程
			
			}
		}
	}
}
package waitnotify;

public class StudentDemo {

	public static void main(String[] args) {
		// 创建资源对象
		Student s = new Student();
		
		//创建资源对象
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		//创建线程类对象
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		//分别启动线程
		t1.start();
		t2.start();
	}
}

以上例子的成员变量不是私有化,一般成员变量私有化,可以对上面的进行改进

package lastwaitnotify;

public class Student {
	private String name;
	private int age;
	//声明一个变量
	private boolean flag; //默认没有数据,如果是true,则说明有数据
	
	//set(String name,int age)方法,产生数据
	
	//同步机制可以解决数据不一致
	public synchronized void set(String name,int age){
		//等待唤醒机制
		//判断有没有数据
		if(this.flag){  //对于生产者来说,没有数据,则处于等待状态,
			//处于等待状态
			try {
				this.wait();//阻塞方式,立即释放锁 notify
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		//设置学生数据
		this.name = name;
		this.age = age;
	
		//修改标记
		this.flag = true;//有数据
		//通知gt:消费者线程来消费
		this.notify();//唤醒等待状态	
		}
	
	//get()输出数据
	public synchronized void get(){
		if(!this.flag){
			try {
				this.wait() ;//调用的时候,会立即释放锁
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//输出语句
		System.out.println(this.name+"---"+this.age);
		
		//修改标记
		this.flag = false ;//消费者线程
		//通知对方(st线程),消费者线程没有数据类,赶紧来消费
		this.notify() ;//唤醒t1线程....
		}
	
	}
package lastwaitnotify;
//生产者线程
public class SetThread implements Runnable{

	private Student s;
	public SetThread(Student s){
		this.s = s;
	}
	private int i = 0;
	@Override
	public  void run() {
		while(true){
			if(i%2==0){
				//设置学生数据
				s.set("韩庚", 18);
			}else{
				//设置学生数据
				s.set("张靓颖", 16);
			}
			i++;
		}
	}
}
package lastwaitnotify;

public class GetThread implements Runnable{
	private Student s;
	public GetThread(Student s){
		this.s = s;
	}
	
	@Override
	public void run() {
		
		while(true){
		s.get();
		}
	}
}
package lastwaitnotify;

public class StudentDemo {

	public static void main(String[] args) {
		// 创建资源对象
		Student s = new Student();
		
		//创建资源对象
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		//创建线程类对象
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		//分别启动线程
		t1.start();
		t2.start();

	}

}

二.线程组(ThreadGroup)
表示一个线程的集合

1.public final ThreadGroup getThreadGroup()返回该线程的线程组

2.public ThreadGroup(String name)设置线程组名称

3.public Thread(ThreadGroup,Runnable target,String name)给每一个子线程设置线程组名称

package threadgroup;

public class ThreadGroupDemo {
	public static void main(String[] args) {
		//线程组:一个线程的集合
		method1();
		method2();
	}
	//给每一个子线程设置线程名称
	private static void method2() {
		
		//public ThreadGroup(String name)构造一个新线程组
		
		ThreadGroup tg = new ThreadGroup("这是一个新的线程组") ;
		
		//创建资源对象
		MyRunnable t1 = new MyRunnable();
		
		//创建线程类对象,并且将线程组对象作为参数进行传递,就使用Thread类的构造方法
		//public Thread(ThreadGroup group,Runnable target ,String name){}
		Thread th1 = new Thread(tg, t1, "线程1") ;
		Thread th2 = new Thread(tg, t1, "线程2") ;
		
		//public final ThreadGroup getThreadGroup()返回该线程的线程组
		ThreadGroup tg1 = th1.getThreadGroup() ;
		ThreadGroup tg2 = th2.getThreadGroup() ;
		
		System.out.println(tg1.getName());
		System.out.println(tg2.getName());
		
		tg.setDaemon(true) ;//将线程组中的所有的线程都设置为守护线程(后台线程)
		
	}

	private static void method1() {
		// 获取多个线程所在的线程组名称
		
		//创建资源对象
		MyRunnable tg = new MyRunnable();
		
		//创建线程类对象
		Thread t1 = new Thread(tg);
		Thread t2 = new Thread(tg);
		
		//获取t1和t2所在的线程名称
		//获取线程组对象
		//public final ThreadGroup getThreadGroup()返回该线程所属的线程组
		 ThreadGroup tg1 = t1.getThreadGroup();
		 ThreadGroup tg2 = t2.getThreadGroup();
		 
		 String name1 = tg1.getName();
		 String name2 = tg2.getName();
		 
		 //子线程默认的线程组名称:main线程
		 System.out.println(name1);//main
		 System.out.println(name2);//main
		 
		 //所有的线程它的默认线程组名称就是main
		 System.out.println(Thread.currentThread().getThreadGroup().getName());
	}

}
package threadgroup;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		while(true){
			for(int i=0;i<100;i++){
				System.out.println(Thread.currentThread().getName()+"---"+i);
			}
		}		
	}	
}
三.线程池
线程池的好处:节约成本,很多子线程调用完毕不会立即被回收掉,而是会回到线程池中被多次利用!
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

1)public static ExecutorService newFixedThreadPool(int nThreads)

Executors工厂类中的这个方法参数直接指定在当前线程池中有多少个线程
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。

ExecutorsService :接口中的方法

2)Future<?> submit(Runnable task)

<T> Future<T> submit(Callable<T> task)

package threadpool;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		while(true){
			for(int i=0;i<100;i++){
				System.out.println(Thread.currentThread().getName()+":"+i);
			}
		}

	}

}
package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorDemo {
	public static void main(String[] args) {
		//创建线程池对象
		//public static ExecutorService newFixedThreadPool(int nThreads)
		ExecutorService pool = Executors.newFixedThreadPool(2);
		
		//使用submit(Runnable target):提交多个任务
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		
		//结束线程池
		pool.shutdown();		
		
	}
}
多线程实现的方式三(实际开发中很少用)

创建线程池对象 实现callable接口

package callable;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 多线程程序的实现方式3:(实际开发中很少用到!)
 * 	public static ExecutorService newFixedThreadPool(int nThreads)
				Executors工厂类中的这个方法参数直接指定在当前线程池中有多少个线程
		这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnabl	e对象或者Callable对象代表的线程。它提供了如下方法
		
		ExecutorsService :接口中的方法
		<T> Future<T> submit(Callable<T> task)
 * @author Apple
 */
public class CallableDemo {

	public static void main(String[] args) {
		// 创建线程池对象
		ExecutorService threadpool = Executors.newFixedThreadPool(2);
		
		//提交Callable任务
		threadpool.submit(new MyCallable());
		threadpool.submit(new MyCallable());
		//结束线程池
		threadpool.shutdown();

	}

}
package callable;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Object> {

	@Override
	public Object call() throws Exception {
		for(int i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
		return null;
	}

}
四.多线程中的匿名内部类的方式

格式:new 类名(具体类,抽象类),接口(){
  重写/实现方法;
   }

匿名内部类的本质:继承了该类或者是实现该接口的子类对象!

public class ThreadDemo {
	public static void main(String[] args) {
		
		//继承自Thread类
		new Thread(){
			@Override
			public void run() {
				//for循环
				for(int x = 0 ; x <100 ; x ++){
					System.out.println(Thread.currentThread().getName()+":"+x);
				}
			}
		}.start() ;//启动线程
		
		
		//Runnable接口的方式
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				//for循环
				for(int x = 0 ; x < 100 ; x ++){
					System.out.println(Thread.currentThread().getName()+":"+x);
				}
			}
		}).start() ;
}
五.定时器Timer

常用的方法:

public void schedule(TimerTask task,Date time)安排在指定的时间执行指定的任务

public void schedule(TimerTask task, long delay)在多少毫秒后执行指定任务

public void schedule(TimerTask task, long delay, long period)在多少毫秒后,执行任务,并且每个多少毫秒重复执行

public void cancel()终止此计时器,丢弃所有当前已安排的任务  

TimeTask是一个抽象类,自定义一个类实现它

练习:在指定的时间内删除当前项目下Demo文件夹中的内容

package timer;

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest {

	public static void main(String[] args) throws ParseException {
		//创建Timer定时器
		Timer t = new Timer();
		
		//定义一个文本日期格式
		String dateStr = "2017-12-8 16:50:00";
		//解析成Date对象
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		//解析方法
		Date date = sdf.parse(dateStr);
		
		//public void schedule(TimerTask task,Date time)安排在指定的时间执行指定的任务
		//调用定时器在指定的时间内执行某个任务
		t.schedule(new DeleteFolder(), date);
		
		

	}

}
//删除Demo文件夹
class DeleteFolder extends TimerTask{

	@Override
	public void run() {
		// 封装当前项目下的Demo文件
		File srcFolder = new File("demo");
		deleteFolder(srcFolder);
	}
	
	//递归删除
	private void deleteFolder(File srcFolder) {
		// 获取当前strFolder下面的所以文件及文件夹
		File[] fileArray = srcFolder.listFiles();
		//对该对象非空判断
		if(fileArray!=null){
			//增强for遍历
			for(File file : fileArray){
				//判断file对象是否是文件夹
				if(file.isDirectory()){
					//继续回到删除目录的方法
					deleteFolder(file);
				}else{
					//不是目录,是文件直接删除
					System.out.println(file.getName()+"---"+file.delete());
				}
			}
			System.out.println(srcFolder.getName()+"---"+srcFolder.delete());
		}
		
	}
	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值