线程创建
Thread
类
Thread
实现 Runnable
;符合模板设计模式(声明一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑);即继承Runnable 接口,实现其抽象方法run,而run方法是由JVM线程调用并运行Runnable
接口
Thread
类来实现线程,就会打破原有的类间的父子关系;而通过实现 Runnable
接口可避免此问题,接口可继承多个Runnable
接口,可将业务逻辑与线程管理分离开,符合策略设计模式Thread 和 Runnable 的区别与联系
Thread:负责线程本身的功能
Runnable:专注于业务逻辑的实现
两者职责功能不同,不具有可比性
Runnable中的run方法,即使不在Thread中使用,他在其他地方照样也能使用,并不能说Runnable就是一个线程
线程的状态:
其他信息:
extends Thread
与 implements Runnable
创建线程方式
// 思路一:多线程处理,启动三个线程,分别处理同一个问题
/**
* 排队叫号系统;假设当前10人排队,共3个窗口工作
*/
public class TicketWindowTest extends Thread{
private int maxPersonCount ; // 最大排队人数
@Override
public void run(){
while(true){
if(maxPersonCount > 10){ // 最多10人,超过不处理
System.out.println("stop service");
break ;
}
System.out.println(Thread.currentThread().getName()+"-"+maxPersonCount++);
}
}
/**
* @param args
*/
public static void main(String[] args) {
TicketWindowTest thread = new TicketWindowTest();
TicketWindowTest thread1 = new TicketWindowTest();
TicketWindowTest thread2 = new TicketWindowTest();
thread.start();
thread1.start();
thread2.start();
}
}
// console
Thread-0-0
Thread-1-0
..........
// 分析:从运行结果中可以得出会出现同一个人被两个或多个窗口同时或重复叫号的问题
// 改进:将maxPersonCount 改为 类的属性,为该类创建的所有对象共享
// 思路一:劣势是同一段业务逻辑被不同的线程多次执行
// 思路二:只建立一个线程,启动三次
// 运行时抛出 IllegalThreadStateException 异常
// 从 Thread 类中的 start 方法可以得出一个线程只能被启动一次
// 此思路不可行
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
}
// 思路三:使用Runnable ,Runnable 的作用,将业务逻辑与线程执行进行分离;且类只允许单继承,但可以多实现,所以实现多线程时建议使用 Runnable 接口;
public class TicketWindowRunnableTest implements Runnable {
private static int maxPersonCount ;
public void run() {
while(true){
if(maxPersonCount > 10){
break ;
}
System.out.println(Thread.currentThread().getName()+"-"+maxPersonCount++);
}
}
}
public class TicketWindowRunnableTestMain {
/**
* @param args
*/
public static void main(String[] args) {
TicketWindowRunnableTest test = new TicketWindowRunnableTest();
Thread thread1 = new Thread(test);
Thread thread2 = new Thread(test);
Thread thread3 = new Thread(test);
thread1.start();
thread2.start();
thread3.start();
}
}
// 从运行结果上看,只有一个线程在运行,其他没有;没实现多线程?否,将 maxPersonCount 设置为全局变量,为多个对象共享;由于线程的运行机制,有JVM决定何时运行,可能导致每次运行结果均不一致,Thread-0 执行完毕前,Thread-1 是否已启动并运行
Thread-0-0
Thread-0-1
Thread-0-2
Thread-0-3
Thread-0-4
Thread-0-5
Thread-0-6
Thread-0-7
Thread-1-8
Thread-0-9
Thread-1-10
示例:多线程之间的数据共享引发的安全问题
// 将上面思路三中
public class TicketWindowRunnableTest implements Runnable {
private int maxPersonCount ; // 去掉 static 修饰
}
// 运行后输出结果
Thread-1-0
Thread-0-1
Thread-2-1
Thread-0-2
Thread-1-2
Thread-2-2
Thread-2-3
Thread-1-4
Thread-0-3
Thread-0-5
Thread-2-6
Thread-1-5
Thread-1-7
Thread-2-7
Thread-0-7
Thread-0-9
Thread-2-8
Thread-1-9
Thread-2-11
Thread-1-10
Thread-0-12
public class TicketWindowRunnableTest implements Runnable {
private int maxPersonCount ;
private Object obj = new Object(); // 任意对象
public void run() {
while(true){
synchronized (obj) { // 同步代码块
if(maxPersonCount > 10){
break ;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-"+maxPersonCount++);
}
}
}
}
// 运行输出结果:不会再出现重复号码或超过最大限度的编号
// 将 synchronized 加到 多线程 访问的共享的数据上
public class TicketWindowRunnableTest implements Runnable {
private int maxPersonCount ;
private Object obj = new Object();
public void run() {
while(true){
if(ticket()){
break ;
}
}
}
public boolean ticket(){
synchronized (obj) {
if(maxPersonCount > 10){
return true ;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-"+maxPersonCount++);
}
return false ;
}
}
run()
方法上添加 synchronized
关键字修饰?
run()
方法;此时其他线程同样想要执行,但即使获取了CPU,未获取到锁,所以只能等待;直到该线程运行结束释放了锁,其他线程才可以执行,如上面的例子,其他线程获取了锁, run()
方法中的判断条件已经不满足了,直接退出this
锁与static
锁
/**
* 假设类中方法间不是 this 锁
* 那么启动 thread1 与 thread2 时,methodA 与 methodB 是可以同时执行的
* 所以,类中方法间是 this 锁
*/
public class ThreadTest1 {
public synchronized void methodA(){
System.out.println("methodA run");
while(true){
// 死循环
// 若此处使用其他方法,如 i++<50 ,起不到效果,会出现两个方法都有结果数据
// 但是,是methodA执行完毕后再执行的methodB
}
}
public synchronized void methodB(){
System.out.println("methodB run");
while(true){
}
}
/**
* @param args
*/
public static void main(String[] args) {
final ThreadTest1 test = new ThreadTest1();
Thread thread1 = new Thread(new Runnable(){
public void run() {
test.methodA();
}
});
Thread thread2 = new Thread(new Runnable(){
public void run() {
test.methodB();
}
});
thread1.start();
thread2.start();
}
}
/**
* obj 是代码块间的锁
* 不同方法间需使用 this 锁
* ticket() 使用 obj 对象锁
* ticketMethod() 使用 synchronized 修饰方法,this 锁
* 运行结果:maxPersonCount 出现 11 ,大于 10
* 理论上不可能出现 11 ,即没有起到数据同步的作用
*/
public class TicketWindowRunnableTest implements Runnable {
private int maxPersonCount ;
private Object obj = new Object();
private boolean flag = true ;
public void run() {
if(flag){
while(true){
if(ticket()){
break ;
}
}
}else{
while(true){
if(ticketMethod()){
break ;
}
}
}
}
public void change() throws InterruptedException{
Thread.sleep(3);
flag = false ;
}
public synchronized boolean ticketMethod(){
if(maxPersonCount > 10){
return true ;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-"+maxPersonCount++);
return false ;
}
public boolean ticket(){
synchronized (obj) {
if(maxPersonCount > 10){
return true ;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-"+maxPersonCount++);
}
return false ;
}
}
public class TicketWindowRunnableTestMain {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
TicketWindowRunnableTest test = new TicketWindowRunnableTest();
Thread thread1 = new Thread(test);
Thread thread2 = new Thread(test);
Thread thread3 = new Thread(test);
thread1.start();
test.change();
thread2.start();
thread3.start();
}
}
// 将上述示例中的
synchronized(object) 改为 synchronized(this) ,解决上述的问题,maxPersonCount 不会出现 11的值
// 方法间应使用 this 锁来同步数据
/**
* 类中方法带有 static 关键字修饰时,加锁使用类锁
* 该类创建的所有对象,共享该类锁
*/
public class TicketWindowStaticRunnable implements Runnable {
private static int max_value = 0 ;
private boolean flag = true ;
public void run() {
if(flag){
while(true){
synchronized (TicketWindowStaticRunnable.class) {
if(max_value > 10){
break ;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"_"+max_value++);
}
}
}else{
while(true){
if(ticket()){
break ;
}
}
}
}
public synchronized static boolean ticket(){
if(max_value > 10){
return true ;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"_"+max_value++);
return false ;
}
public void change() throws InterruptedException{
Thread.sleep(3);
flag = false ;
}
}
public class TicketWindowStaticRunnableMain {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
TicketWindowStaticRunnable test = new TicketWindowStaticRunnable();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
test.change();
t2.start();
}
}