一、线程控制中常用的方法:
①、join方法的使用,代码如下:
package com.weizhaoyang.demo;
/**
* join方法的使用
* 作用:让其主线程等待子线程完成后再执行主线程
*/
public class ThreadJoin {
public static void main(String[] args) {
System.out.println("main thread start");
Thread T1 = new Thread(new MyRunnable(), "AAAA");
T1.start();
try {
T1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread end");
}
static class MyRunnable implements Runnable{
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"执行中");
}
}
}
}
运行的结果如下:
②、设置守护线程
例1、在java中常用的守护线程是GC垃圾回收器, 作用是给其他的线程提供服务的,当主线程程死亡,守护线程自动死亡,只要主线程没有死亡,守护线程会一直去守护它,代码如下:
package com.weizhaoyang.demo;
import java.io.IOException;
/**
* 测试守护线程
*/
public class ThreadDaemon {
public static void main(String[] args) {
System.out.println("main Thread start");
Thread thread = new Thread(new MyRunnable());
thread.setDaemon(true);//设置守护线程
thread.start();
/* try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
//I/O阻塞,等待主线程去输入,一旦输入,然后守护线程就结束了,否则就等待
try {
System.out.println("请输入内容");
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("main thread end");
System.out.println(thread.getState());
}
static class MyRunnable implements Runnable{
public void run() {
for(;;){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行GC操作准备.....");
}
}
}
}
运行的结果如下:当输入内容和者敲回车的话,守护线程会立刻结束。
守护线程用的场景 :比如某几秒钟想要统计人流量或者流量的数据,这时把它可以设置为守护线程,作用:为其非守护线程(主线程和其他的子线程)提供服务的。
三、sleep方法
主动进入阻塞状态,调用sleep方法,JDK5以后推荐使用TimeUtil枚举睡眠
四、yield方法
1、执行yield方法,让当前线程设置为就绪状态
2、当完成一个方法的时候,可以将处理器控制权让出去
3、不保证让出去,其他线程就能够马上得到控制权,可能自己又一次拿到了控制权
只是提高了其他的线程拿到时间片的概率提高。
package com.weizhaoyang.demo;
public class ThreadYield {
public static void main(String[] args) {
//接口的匿名参数
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"执行"+i);
}
}
},"AAAA");
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<5;i++){
if(i==0){
//当T2线程中的i=2的时候,t1让出来,让t2运行
t1.yield();
}
System.out.println(Thread.currentThread().getName()+"执行"+i);
}
}
},"BBBB");
//启动线程
t1.start();
t2.start();
t1.yield();
}
}
运行的结果如下:
五、线程的优先级:proiority,只是说让线程获取时间片的几率大大提高了
范围1-10,中间的优先级为5,推荐使用Thread的三个镜头常量,适应不同的操作系统。
代码如下:
package com.weizhaoyang.demo;
/**
* 线程的优先级
*/
public class ThreadPriority {
public static void main(String[] args) {
Thread t1=new Thread(new MyRunnable(),"AAAA");
Thread t2=new Thread(new MyRunnable1(),"BBBB");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
static class MyRunnable implements Runnable{
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+" 执行中");
}
}
}
static class MyRunnable1 implements Runnable{
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+" 执行中");
}
}
}
}
运行如下:线程优先级,提高线程优先于其他线程的几率,并不是按照你设定的去执行。
二、线程同步
当多个线程访问同一个资源时,加synchronized对象对资源进行加锁
①、制造一个线程安全问题的例子:沉睡的话 可以增大线程安全问题几率提高
package com.weizhaoyang.safe;
/**
* 线程安全问题:多个线程共享同一个资源导致的
*/
public class ThreadSafe1 {
public static void main(String[] args) {
//创建三个卖票窗口
MyThread t1=new MyThread("窗口1");
MyThread t2=new MyThread("窗口2");
MyThread t3=new MyThread("窗口3");
t1.start();
t2.start();
t3.start();
}
static class MyThread extends Thread{
public MyThread(String name){
super(name);
}
//让所有的实例共享这个资源
private static int count=10;
@Override
public void run() {
while(count>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
String tname = Thread.currentThread().getName();
System.out.println(tname+"卖出了"+(count--)+"票");
}
}
}
运行结果如下:这就是在睡眠的时候出现了线程的安全性问题,本来是10张票,却卖出12张票。也就是说在阻塞的过程中,窗口1,2同时拿到了还没有减的这张票(0),而且这个“0”票是超卖的票,原因当窗口3进来拿到票了,而没有减,当窗口2进来的时候,把票减了,这时窗口2和窗口1都拿到了“0”票,这就出现了线程的安全性的问题,没有保证原子性。
如果不加static的话,三个窗口都会分别卖出10张票,因为每创建一个对象就会有一个count变量为10的成员,属于实例中的成员,局部变量不会出现线程安全的问题。
上面使用的是继承的方式,如果用接口的话,更好,因为接口是多实现的,继承是单继承的
例子②,代码如下:
package com.weizhaoyang.safe;
public class ThreadSafe2 {
//让所有的实例共享这个资源
private static int count;
public static void main(String[] args) {
Thread t1=new Thread(new MyThread());
Thread t2=new Thread(new MyThread());
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count="+count);
}
static class MyThread implements Runnable{
@Override
public void run() {
for(int i=0;i<1000000;i++){
count++;
}
}
}
}
运行的结果如下:这也是线程的安全的问题,说明很多数据出现了线程的安全性的问题。
解决方案:
用到线程同步操作:既然保证count的原子性
synchronized有三种加锁的方式,在jdk1.5的时候用的比较多,但是现在已经被并发编程的包给替代掉了
1、同步块:
synchronized:保证在同一个时间片上不能有其他线程同时执行 。
当其中一个线程拿到锁之后,就执行count++,然后执行完,就释放锁,然后另一个线程来执行,否则继续等待锁。并且仅有一个线程拿到锁。
同步块里传的参数是对象,代表锁的标识。
package com.weizhaoyang.sync;
import com.weizhaoyang.safe.ThreadSafe2;
/**
* 使用同步块解决线程安全的问题
*/
public class SyncBlock {
//让所有的实例共享这个资源
private static int count;
private static Object lock=new Object();
public static void main(String[] args) {
Thread t1=new Thread(new MyThread());
Thread t2=new Thread(new MyThread());
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count="+count);
}
static class MyThread implements Runnable{
@Override
public void run() {
for(int i=0;i<1000000;i++){
synchronized (lock){
count++;
}
}
}
}
}
结果如下:
2、在实例上加锁
package com.weizhaoyang.sync;
/**
* 使用同步关键字枷锁对象的实例方法
*/
public class SynInstance {
//让所有的实例共享这个资源
private static int count;
public static void main(String[] args) {
MyThread myThread=new MyThread();
Thread t1=new Thread(myThread);
Thread t2=new Thread(myThread);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count="+count);
}
static class MyThread implements Runnable{
public synchronized void trace(){
count++;
}
@Override
public void run() {
for(int i=0;i<1000000;i++){
trace();
}
}
}
}
运行的结果如下:
3、类锁,代码如下:
package com.weizhaoyang.sync;
/**
* 使用同步关键字枷锁对象的实例方法
*/
public class SynInstance {
//让所有的实例共享这个资源
private static int count;
public static void main(String[] args) {
Thread t1=new Thread(new MyThread());
Thread t2=new Thread(new MyThread());
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("count="+count);
}
static class MyThread implements Runnable{
public static synchronized void trace(){
count++;
}
@Override
public void run() {
for(int i=0;i<1000000;i++){
trace();
}
}
}
}
运行的结果如下:
一般使用类锁比较好
总结:不管在对象上加锁,类上加锁,实例上加锁,只要持有的是同一把锁就可以解决线程 安全的问题,明天写重入锁,非公平,公平,读写锁。