简单的多线程

1.线程

1.特点

多个线程之间是抢cpu的关系有随机性

2.命名

主线程的名字:main 子线程的名字:从Thread-0开始命名

setName可以命名

3.开启

start()方法,来开启线程,继而实现run方法的自动调用.

4.注意

手动调用run的时候,他失去了任务区的功能,变成了一个普通的方法.

2.Runnable接口

1.作用

线程和任务分离 ,哪个线程需要工作,就将任务交给谁

2.调用

Thread内部默认有一个run,又通过实现接口类传入一个run,优先调用传入的run。

3.用法

Thread t = new Thread(new Runnable(实现接口的对象));

3.同步代码块

1.关键字和特点

1.关键字

synchronized关键字(隐式同步)

1、synchronized关键字的作用域有二种:
1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象
的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的
一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。
这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时
访问相同类的另一个对象实例中的synchronized方法;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问
这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,
表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/区块/},它的作用域是当前对象;

3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){}
在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定
它的某个方法为synchronized方法;

2.特点

但是每次使用都要判断,所以降低了效率

使用时有两个及其以上线程共享一个数据

2.使用方法

synchronized((对象)){

同步的代码

}

解释:在某一段任务中,同一时间只允许一个线程执行任务,其他的线程即使抢到了cpu,也无法进入当前的任务区间,只有当当前的线程将任务执行完后,其他的线程才能有资格进入

3.锁的要求

1.要求

1.必须是对象 2.必须保证被多个线程共享

注意: 静态同步函数的锁是其所属类的字节码文件对象

2.例子

1.一个普通的对象 2.当前对象的引用–this 3.类的字节码文件

注意:字节码文件的使用范围太大,一般不建议使用.

public class Demo4 {
	public static void main(String[] args) {
		//任务对象
		Ticket1 ticket = new Ticket1();
		//将任务与线程绑定
		Thread t0 = new Thread(ticket);
		Thread t1 = new Thread(ticket);
		//开启线程
		t0.start();
		t1.start();
	}
}

//任务类

class Ticket1 implements Runnable{
     int sum = 20;
     boolean flag = true;
     //让Object类型的对象临时充当锁
     Object object = new Object();
	public void run() {
		while (flag) {
			synchronized (object) {//线程互斥
				if (sum > 0) {
					System.out.println("剩余 票数:"+ --sum);
				}else {
					flag = ! flag;
				}
			}
		}
	}
}

5.等待唤醒(执行顺序)

1.同步任务加同一把锁

2.等待唤醒机制

1.函数:

notify()/notifyAll()/wait()

wait():
让当前的线程变成等待的状态,放入一个池子(线程容器),失去了抢cpu的能力,.等待唤醒(锁相当于给当前的线程做了一个标记)

notify():
让当前的线程从等待状态唤醒,相当于从池子中取出线程.(唤醒的是同一把锁下的任意一个线程)
notifyAll():
唤醒的是同一把锁下的所有线程

注意1.锁就是标记

2.必须要跟synchronize一起用的,否则会异常

2.优化分离
public class Demo8 {
	public static void main(String[] args) {
		//两个线程:输入线程和输出线程
		//1.准备数据
		Des2 des = new Des2();
		//2.准备两个任务
		Input2 input = new Input2(des);
		Output2 output = new Output2(des);
		//3.准备两个线程
		Thread in = new Thread(input);
		Thread out = new Thread(output);
		//4.开启线程
		in.start();
		out.start();
	}
}
//创建数据类
class Des2{
	String name;
	String sex;
	boolean flag;//控制唤醒和等待状态的切换
	

//负责输入
public void setData(String name,String sex) {
	if (flag == true) {//当flag值为true,就让当前的线程处于等待状态
		try {
			wait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}//当执行这行代码的时候,这里对应的是哪个线程,就操作的是哪个线程
	}
	
	this.name = name;
	this.sex = sex;
	
	flag = !flag;
	
	notify();//唤醒的是通一把锁下的线程,因为现在只有一个输入线程,一个输出线程.所以这里唤醒的是输出线程
	//当线程池中没有被当前的锁标记的线程可唤醒时,我们成为空唤醒,空唤醒不影响程序的执行.
}
//负责输出
public void getData() {
	if (flag == false) {//让输出线程等待
		try {
			wait();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	System.out.println("姓名:"+name+"    性别:"+sex);
	
	flag = ! flag;
	
	notify();//唤醒的是输入线程
}

}

//两个任务区:输入任务,输出任务
class Input2 implements Runnable{
	Des2 des = null;
	public Input2(Des2 des) {
		super();
		this.des = des;
	}
	public void run() {
		int i=0;
		while (true) {
			/*
			 * 需要给输入任务和输出任务同时加一把锁,保证两个任务之间是同步的
			 * 给两个任务加一把锁:可以是des或者Object.class
			 * 分析:
			 * 不建议使用Object.class:由于Object的使用范围太大,可能造成不必要的错误.
			 * 使用des最合适,因为他只被当前的两个任务共享.
			 * 
			 *注意:对于当前的情况只给一个线程加锁,无法实现两个线程的同步.
			 */
			

		synchronized (des) {
			
			if (i == 0) {
				des.setData("万表哥", "男");
			}else {
				des.setData("蔡表妹", "女");
			}
			
			i=(i+1)%2;
			
		}
	}
}

}

class Output2 implements Runnable{
	Des2 des = null;
	public Output2(Des2 des) {
		super();
		this.des = des;
	}
	public void run() {
		while (true) {
			synchronized (des) {
				des.getData();
			}
		}
	}
}

6.线程的停止

1.标志结束线程

public class Demo14 {
	public static void main(String[] args) {
		MyTest myTest = new MyTest();
		Thread thread  = new Thread(myTest);
		thread.start();
		
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
		e.printStackTrace();
		}
		
	int i =0;
		while (true) {
			if (++i == 10) {//当i==10的时候,我就让子线程结束,让flag=false
				myTest.flag = false;
				
				break;//主线程结束
			}
		}
	}
}

class MyTest implements Runnable{
	boolean flag = true;
	public void run() {
		while (flag) {
			System.out.println(Thread.currentThread().getName()+"   明天吃西瓜");
		}	
	}
}

2.调用interrupt

public class Demo14 {
	public static void main(String[] args) {
		MyTest myTest = new MyTest();
		Thread thread  = new Thread(myTest);
		thread.start();
		

	try {
		Thread.sleep(100);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	int i =0;
	while (true) {
		if (++i == 10) {//当i==10的时候,我就让子线程结束,直接调用interrupt方法
			thread.interrupt();
			
			break;//主线程结束
		}
	}
}

}

class MyTest implements Runnable{
	boolean flag = true;
	public synchronized void run() {
		while (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				flag = false;
			}
			System.out.println(Thread.currentThread().getName()+"   明天吃西瓜");
		}	
	}
}

7.守护线程

当程序调用setDaemon方法时,并且将参数设置成true.当前线程就变成了守护线层.

注意:这个方法一定要在start方法之前调用

8.join方法

join()方法:

原理:线程一旦调用了join方法,他的优先级会高于主线程.主线程会等当前的线程执行完后再去执行.

注意点:优先级只比main线程的高.对其他的线程没有影响.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值