Java多线程(二)

一、创建多线程

第二种方式:实现 Runnable 接口

步骤:

1,定义类实现Runnable接口;

2,覆盖Runnable接口中的 run 方法;

       目的:将线程要运行的代码存放在该run方法中。

3,通过Thread 类建立线程对象;

4,将Runnable 接口的子类对象作为实际参数传递给Thread类的构造函数

5,调用Thread类的start 方法开启线程并调用Runnable接口子类的run方法。

二、创建多线程两种方式(继承Thread 和实现Runnable)的区别:

两种方式的区别:(线程代码存放位置不同)

继承Thread类:线程代码存放在Thread子类run方法中;

实现Runnable:线程代码存放在接口的子类的run方法中;

Java只支持单继承(即一个类只能有一个父类),但支持多实现。如果一个类本身已经继承了一个父类,如果想通过继承Thread类来启动多线程,将不可行。因此此刻已经超出继承父类的个数了。此时可以通过实现Runnable接口,来启动多线程。

实现方式的优点:避免单继承的局限性

在定义线程的时候,推荐使用实现的方式创建线程 

public class TicketThread {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		
		t1.start();
		t2.start();
		t3.start();
	}
}


class Ticket implements Runnable{
	private static int ticket = 50;
	
	public void run() {
		while (true) {
			if (ticket==0) {
				break;
			}
			if (ticket>0) {
				System.out.println(Thread.currentThread().getName()+ " sale: "+ ticket--);
			}
		}			
	}
}

执行结果:

Thread-0 sale: 50
Thread-1 sale: 48
Thread-2 sale: 49
Thread-1 sale: 46
Thread-1 sale: 44
Thread-0 sale: 47
Thread-0 sale: 42
Thread-0 sale: 41
Thread-0 sale: 40
Thread-0 sale: 39
Thread-0 sale: 38
Thread-0 sale: 37
Thread-0 sale: 36
Thread-0 sale: 35
Thread-0 sale: 34
Thread-0 sale: 33
Thread-0 sale: 32
Thread-0 sale: 31
Thread-0 sale: 30
Thread-0 sale: 29
Thread-0 sale: 28
Thread-1 sale: 43
Thread-2 sale: 45
Thread-2 sale: 25
Thread-2 sale: 24
Thread-2 sale: 23
Thread-2 sale: 22
Thread-2 sale: 21
Thread-1 sale: 26
Thread-0 sale: 27
Thread-0 sale: 18
Thread-0 sale: 17
Thread-0 sale: 16
Thread-0 sale: 15
Thread-1 sale: 19
Thread-2 sale: 20
Thread-2 sale: 12
Thread-2 sale: 11
Thread-2 sale: 10
Thread-2 sale: 9
Thread-2 sale: 8
Thread-2 sale: 7
Thread-2 sale: 6
Thread-2 sale: 5
Thread-2 sale: 4
Thread-2 sale: 3
Thread-2 sale: 2
Thread-1 sale: 13
Thread-0 sale: 14
Thread-2 sale: 1

三、多线程同步代码块

问题描述:50张票,每执行一次run方法,票数减一,直至ticket 为0;代码描述情景:

public class TicketThread {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);		
		t1.start();
		t2.start();
		t3.start();
	}
}

class Ticket implements Runnable{
	private static int ticket = 50;

	public void run() {
		while (true) {
			if (ticket>0) {
				//此处用异常模拟系统切换到其他线程中执行操作
					try {Thread.sleep(10);} catch (Exception e) {}
					System.out.println(Thread.currentThread().getName()+ " sale: "+ ticket--);
				}			
			}			
	}
}

执行结果如下:

Thread-2 sale: 50
Thread-1 sale: 49
Thread-0 sale: 48
Thread-1 sale: 47
Thread-2 sale: 46
Thread-2 sale: 45
Thread-1 sale: 44
Thread-0 sale: 43
Thread-1 sale: 42
Thread-2 sale: 41
Thread-1 sale: 40
Thread-2 sale: 39
Thread-0 sale: 38
Thread-2 sale: 37
Thread-1 sale: 36
Thread-0 sale: 35
Thread-1 sale: 34
Thread-2 sale: 33
Thread-1 sale: 32
Thread-2 sale: 31
Thread-0 sale: 30
Thread-2 sale: 29
Thread-1 sale: 28
Thread-0 sale: 27
Thread-1 sale: 26
Thread-2 sale: 25
Thread-0 sale: 24
Thread-2 sale: 23
Thread-1 sale: 22
Thread-1 sale: 21
Thread-2 sale: 20
Thread-1 sale: 19
Thread-2 sale: 18
Thread-0 sale: 17
Thread-2 sale: 16
Thread-1 sale: 15
Thread-1 sale: 14
Thread-2 sale: 13
Thread-0 sale: 12
Thread-2 sale: 11
Thread-1 sale: 10
Thread-0 sale: 9
Thread-1 sale: 8
Thread-2 sale: 7
Thread-2 sale: 6
Thread-1 sale: 5
Thread-0 sale: 4
Thread-1 sale: 3
Thread-2 sale: 2
Thread-0 sale: 1
Thread-1 sale: 0
Thread-2 sale: -1

出现了ticket为负数的情况,但是代码中循环只有ticket大于0才执行减一的操作,因此线程出现不安全的情况。

问题原因:

       当多条语句操作同一个线程的共享数据时,一个线程对多条语句只执行了一部分操作,而且还没执行完,另一个线程参与进

来,导致共享数据的错误;

同步代码块的格式

           synchronized(对象){

                 // 需要被同步的代码块

           }

场景类似:排队使用独立卫生间

同步的前提:

1,必须要有两个或者两个以上的线程。

2,必须是多个线程使用同一个锁

 

对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

此时可以通过加入同步代码块解决问题,代码如下:

package Thread;

public class TicketThread {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		
		t1.start();
		t2.start();
		t3.start();
	}
}


class Ticket implements Runnable{
	private static int ticket = 50;
	Object obj = new Object();
	public void run() {
		while (true) {
			synchronized (obj) {
				if (ticket>0) {				
					try {Thread.sleep(10);} catch (Exception e) {}
					System.out.println(Thread.currentThread().getName()+ " sale: "+ ticket--);
				}			
			}
		}			
	}
}

执行结果

Thread-2 sale: 50
Thread-2 sale: 49
Thread-2 sale: 48
Thread-2 sale: 47
Thread-2 sale: 46
Thread-2 sale: 45
Thread-2 sale: 44
Thread-2 sale: 43
Thread-2 sale: 42
Thread-2 sale: 41
Thread-2 sale: 40
Thread-2 sale: 39
Thread-2 sale: 38
Thread-2 sale: 37
Thread-2 sale: 36
Thread-2 sale: 35
Thread-2 sale: 34
Thread-2 sale: 33
Thread-2 sale: 32
Thread-2 sale: 31
Thread-2 sale: 30
Thread-2 sale: 29
Thread-2 sale: 28
Thread-2 sale: 27
Thread-2 sale: 26
Thread-2 sale: 25
Thread-2 sale: 24
Thread-2 sale: 23
Thread-2 sale: 22
Thread-2 sale: 21
Thread-2 sale: 20
Thread-2 sale: 19
Thread-2 sale: 18
Thread-2 sale: 17
Thread-2 sale: 16
Thread-2 sale: 15
Thread-2 sale: 14
Thread-2 sale: 13
Thread-2 sale: 12
Thread-2 sale: 11
Thread-2 sale: 10
Thread-2 sale: 9
Thread-2 sale: 8
Thread-2 sale: 7
Thread-2 sale: 6
Thread-2 sale: 5
Thread-2 sale: 4
Thread-2 sale: 3
Thread-2 sale: 2
Thread-2 sale: 1

同步代码块优缺点

优点:解决多线程安全问题;

缺点:多个线程需要判断锁,较为消耗资源;

多线程找安全问题的方法:

1,明确哪些代码是多线程运行代码;

2,明确共享数据;

3,明确多线程运行代码中哪些语句是操作共享数据的;

四、同步函数

将 synchronized 以关键字的形式放在函数定义上,该函数就是同步函数,此时锁是“this

例如:

public class TicketThread {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		
		t1.start();
		t2.start();
		t3.start();
	}
}


class Ticket implements Runnable{
	private static int ticket = 1000;
	
	public void run() {
		while (true) {
			show();
		}			
	}
        //同步函数
	public synchronized void show() {
		if (ticket>0) {				
			try {Thread.sleep(10);} catch (Exception e) {}
			System.out.println(Thread.currentThread().getName()+ " sale: "+ ticket--);
		}	
	}
}

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class  该对象的类型是class

//懒汉式的作用:延迟加载;
class Single {
	
	private static Single s = null;
	
	private Single() {}
	
	public static Single getInstance() 
	{
		//用双重判断的形式,解决同步锁的低效问题
		if (s==null) 
		{
			synchronized (Single.class)
			{
				if (s == null)
				{
					s = new Single();
				}
			}
		}
		return s;
	}
}

猜你喜欢

转载自blog.csdn.net/zjt980452483/article/details/81203955