目录
线程调度:
线程调度: ·分时调度 所有线程轮流使用cpu的使用权,平均分配每个线程占用cpu 的时间。·抢占式调度 优先让优先级高的线程使用cpu,如果线程的优先级相同,那么会随机选择一个(线程随机性) ,java使用的为抢占式调度。
主线程:
主线程:执行主(main)方法的线程
Thread类
Java使用
java.lang.Thread
类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建并启动多线程先学习Thread的两个简单的构造方法:
public Thread(): 无参构造 public Thread(String name) :指定线程名字构造方法:
public Thread(Runnable target)
:分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name)
:分配一个带有指定目标新的线程对象并指定名字。常用方法:
创建线程方式一之继承类创建
代码示例:
//新建的线程类继承线程,重写run方法 /** * 创建线程类的第一种方式继承Thread类 */ public class MyThread extends Thread{ //重写run方法 @Override public void run() { super.run(); for (int i = 0; i <5 ; i++) { System.out.println("run"+i); } } } //测试类,也就是主线程 public class Test { public static void main(String[] args) { //创建Thread类的子类对象 MyThread myThread = new MyThread(); //通过该对象调用start方法启动线程 myThread.start(); //主线程的调用 for (int i = 0; i <5 ; i++) { //结果是两个线程随机抢夺cpu执行权 System.out.println("main"+i);//main0 run0 main1 run1 } } }
结果:
创建线程方式二之Runnable类创建(一般都用这种)
概念:
创建一个类来实现Runnable接口,并实现Runnable接口中的run函数。
任务类实现Runnable,重写run方法
Runnable接口
1)我们发现,Thread类其实已经实现Runnable接口了,Thread类中的run方法就是实现来自Runnable接口的。
2)Runnable接口中,只有一个run方法,而run方法是用来封装线程任务的。因此,这个接口就是专门用来封装线程任务的接口。为了区分和上述的Thread类,
因此,我们可以这样理解:实现该接口的类,称为线程任务类。
构造方法:
public Thread(Runnable target)
:分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name)
:分配一个带有指定目标新的线程对象并指定名字。注意:第二个构造函数的String类型的name是给自己定义的任务类线程起名字,不用在使用setName()给线程起名字。
代码演示:
//任务类实现Runnable,重写run方法 public class MyRunble implements Runnable{ @Override public void run() { int sum=0; for (int i = 0; i <6 ; i++) { sum+=i; } //得到当前线程的名字 System.out.println(Thread.currentThread().getName()); } } //测试类 public class RunbleTest { public static void main(String[] args) { //创建线程任务类对象 MyRunble myRunble = new MyRunble(); //创建线程对象(利用Thread的构造方法,对线程进行命名,而不是通过setname()方法给线程命名) Thread thread1 = new Thread(myRunble, "我是线程1"); Thread thread2 = new Thread(myRunble, "我是线程2"); //启动线程 thread1.start();//我是线程1 thread2.start();//我是线程2 } }
实现RunnobLe接口创建多线程程序的好处:
1.避免了单继承的局限性 一个类只能继承一个类(一个人只能有一个亲爹),类继承了Thread类就不能继承其他的类实现了Runnable接口,还可以继承其他的类,实现其他的接口
2.增强了程序的扩展性,降低了程序的耦合性(解耦) 实现RunnabLe接口的方式,把设置线程任务和开启新线程进行了分离(解耦)实现类中,重写了run方法:用来设墨线程任务 创建Thread类对象,调用start方法:用来开启新线程
线程休眠:
使用Thread类中的sleep()函数可以让线程休眠,函数如下所示:
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
代码演示
//自定义类 public class RunnableSleep implements Runnable{ @Override public void run() { for (int i = 0; i <5 ; i++) { //获取当前线程的名字 System.out.println(Thread.currentThread().getName()+"->"+i); //休眠一秒,程序会暂停一秒运行出结果 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printstacktrace(); } } } } //测试类 public class Test2 { public static void main(String[] args) { RunnableSleep sleep = new RunnableSleep(); Thread thread = new Thread(sleep, "线程一"); thread.start(); } }
结果:
多线程安全
怎样会出现线程安全:
多线程访问了共享的数据,会产生线程安全问题(举个例子,就是卖票,有四个窗口在卖,但是突然出现了几个窗口卖同一张票,这时这几个窗口就访问了共享的数据(相同的一张票),就出现了线程安全问题)。
怎么解决该问题?
当cpu执行某个线程时,就只让该线程访问共享的数据,其他线程不被允许。这叫线程同步。
怎么保证线程安全:
同步代码块:
加同步格式:
synchronized( 需要一个任意的对象(锁) ){ 代码块中放操作共享数据的代码。 }注意:
同步代码块上的锁,可以是随便任意的一个对象。你可以这样新建或者不新建
Object obj=new Object(); //或者这样 synchronized("随便叫什么都行"){ }注意: 1.通过代码块中的锁对象,可以使用任意的对象
2.但是必须保证多个线程使用的锁对象是同一个
同步方法:
格式:
public synchronized void method(){ 可能会出现线程安全的代码 }
静态同步方法:
以上三种方法的代码演示
/**
* 卖票问题
*
* 卖票案例出现了线程安全问题卖出了不存在的票和重复的票
* 解决方法1.同步代码块
* 2.同步方法
* 3.静态同步方法
*
*/
public class MaiPiao implements Runnable{
static int number=100;
//新建一个锁对象
Object obj=new Object();
@Override
public void run() {
while (true){
staticfanfa();
}
//1.同步代码块解决线程安全问题
/* //死循环一直卖票
while (true){
synchronized (obj){
if (number>0){
//休眠,为了让别的线程有机会运行该程序
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printstacktrace();
}
//打印当前线程的名字和票号
System.out.println(Thread.currentThread().getName()+"===票号"+number);
number--;
}
}
}*/
}
//3.静态同步方法
public static synchronized void staticfanfa(){
if (number>0){
//休眠,为了让别的线程有机会运行该程序
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printstacktrace();
}
//打印当前线程的名字和票号
System.out.println(Thread.currentThread().getName()+"===票号"+number);
number--;
}
}
//2.同步方法
public synchronized void fanfa(){
if (number>0){
//休眠,为了让别的线程有机会运行该程序
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printstacktrace();
}
//打印当前线程的名字和票号
System.out.println(Thread.currentThread().getName()+"===票号"+number);
number--;
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
//创建线程类任务对象
MaiPiao maiPiao = new MaiPiao();
//创建线程对象
Thread thread = new Thread(maiPiao,"一号");
Thread thread2 = new Thread(maiPiao,"二号");
Thread thread3 = new Thread(maiPiao,"三号");
Thread thread4 = new Thread(maiPiao,"四号");
//启动线程
thread.start();
thread2.start();
thread3.start();
thread4.start();
}
}
锁升级
使用Lock来解决线程安全问题
代码举例
//任务类 public class Task implements Runnable{ int number=100; //锁对象 Lock lock = new reentrantlock(); @Override public void run() { while (true){ //加锁 lock.lock(); if (number>0){ //休眠,为了让别的线程有机会运行该程序 try { Thread.sleep(1); } catch (InterruptedException e) { e.printstacktrace(); } //打印当前线程的名字和票号 System.out.println(Thread.currentThread().getName()+"===票号"+number); number--; } //释放锁 lock.unlock(); } } } //测试类 public class Test2 { public static void main(String[] args) { //创建线程类任务对象 Task task = new Task(); //创建线程对象 Thread thread = new Thread(task,"一号"); Thread thread2 = new Thread(task,"二号"); Thread thread3 = new Thread(task,"三号"); Thread thread4 = new Thread(task,"四号"); //启动线程 thread.start(); thread2.start(); thread3.start(); thread4.start(); } }