JVM:JAVA虚拟机识别main 方法(主线程)。
JVM是多线程的,至少有2个线程。
要实现多线程程序需要开启进程。而开启进程需要创建系统资源,但java语言不能创建系统资源,只有c/c++可以。java中提供一个类:Thread类。
Java实现多线程程序的步骤:
1)将类声明为Thread的子类
2)该类重写Thread中的run()方法
3)在主线程进行该自定义线程类的对象创建
例子(子类继承Thread类并重写run方法):
public class Test extends Thread{
@Override
public void run() {
//每个线程都需要执行这里的代码,想写啥写啥
//一般写的是IO操作、循环语句或者一些费时操作
super.run();
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread t1 = new MyThread() ;
t1.start();
MyThread t2 = new MyThread() ;
t2.start();
}
}
执行上面程序两次可以得到不同的结果,可以看到多线程是抢占资源的。
Thread 类提供了一些方法:
public final void setName(String name):给线程起名称public final String getName() :获取线程名称
注意:这些方法都是被final修饰的!!!
static Thread |
currentThread() 返回对当前正在执行的线程对象的引用。 |
void |
setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 |
形式参数为true时表示为启动守护线程。守护线程必须在启动线程前调用。守护线程在被守护线程执行完后不会立即死去,而是执行一段时间后才死去。
代码:
package thread;
/**
* 守护线程
* @author malaganguo
*
*/
public class Test2 {
public static void main(String[] args) {
//创建两个子线程
ThreadDemo td1 = new ThreadDemo() ;
ThreadDemo td2 = new ThreadDemo() ;
//线程命名
td1.setName("守护线程1");
td2.setName("守护线程2");
//创建守护线程
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();
//被守护的主线程命名
Thread.currentThread().setName("被守护线程");
for(int x = 0 ;x < 5 ;x++) {
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
//创建一个线程类
class ThreadDemo extends Thread {
@Override
public void run() {
for(int x = 0 ;x < 100 ;x++) {
System.out.println(getName()+":"+ x);
}
super.run();
}
}
结果:
貌似首先执行的一定是被守护的线程。
线程优先级:线程优先级默认为5,即大家的优先级都是相同的,我们也可以设置优先级,下面是设置线程优先级的3个字段:
public static final int MAX_PRIORITY 10 最大优先级
public static final int MIN_PRIORITY 1 最小优先级
public static final int NORM_PRIORITY 5 默认优先级
与线程优先级相关的两个方法:
public final int getPriority()返回线程的优先级。
public final void setPriority(int newPriority)更改线程的优先级。 优先级越大,被抢先执行的概率越大。
public final void join():等待该线程终止 。编译时期异常interruputedException 中断异常,try catch解决
package thread;
/**
* 等待线程终止
* 设置优先级
* @author malaganguo
*
*/
public class ThreadJoinTest {
public static void main(String[] args) {
//创建3个对象
ThreadJoin tj1 = new ThreadJoin();
ThreadJoin tj2 = new ThreadJoin();
ThreadJoin tj3 = new ThreadJoin();
//命名
tj1.setName("先执行者");
tj2.setName("竞争者1");
tj3.setName("竞争者2");
//先执行优先执行者
tj1.start();
try {
tj1.join();//等待tj1线程执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
//设置优先级
tj2.setPriority(10);
tj3.setPriority(1);
//执行竞争者
tj2.start();
tj3.start();
}
}
static void |
sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
static void |
sleep(long millis, int nanos) 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
public final void stop() ;强迫线程停止执行。 不会执行了 (过时了),方法能使用
public void interrupt()中断线程。 表示中断线程的一种状态
void |
wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 |
package thread;
import java.util.Date;
class ThreadSleep extends Thread {
@Override
public void run() {
super.run();
for (int x = 0; x < 20; x++) {
System.out.println(getName() + ":" + x + ",日期是:" + new Date());
// 休眠1000毫秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadSleepDemo {
public static void main(String[] args) {
// 创建两个子线程
ThreadSleep ts1 = new ThreadSleep();
ThreadSleep ts2 = new ThreadSleep();
// 起名字
ts1.setName("进程1");
ts2.setName("进程2");
ts1.start();
ts2.start();
}
}
public static void yield()暂停当前正在执行的线程对象,并执行其他线程
多线程中遇到的问题
举个栗子:我们假设一个线程是一个售票窗口,我们创建三个线程,这三个线程负责来销售100张电影票。
售票类代码:
class SeilTicket extends Thread {
private int ticket = 100 ;
@Override
public void run() {
while(true) {
//加入线程睡眠,模拟延迟
try {
Thread.sleep(100);//延迟时间越长,对CPU原子性操作的抵抗作用越强
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0 ) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticket--) + "张票" );
}
}
}
}
创建3个售票对象作为窗口 进行售票的代码:
//创建3个资源对象
SeilTicket st1 = new SeilTicket() ;
SeilTicket st2 = new SeilTicket() ;
SeilTicket st3 = new SeilTicket() ;
st1.setName("窗口1");
st2.setName("窗口2");
st3.setName("窗口3");
st1.start();
st2.start();
st3.start();
这样售票的结果:
现在创建一个资源类,给他分配3个线程:
//创建1个资源类对象
SeilTicket st = new SeilTicket() ;
//分配3个新的线程对象
Thread th1 = new Thread(st, "线程1") ;
Thread th2 = new Thread(st, "线程2") ;
Thread th3 = new Thread(st, "线程3") ;
th1.start();
th2.start();
th3.start();
这样售票的结果:
可以看到由于CPU的原子性还是会售出重复的票,并没有得到解决。
这属于多线程安全问题!
多线程安全问题的隐患条件?
1)是否是多线程环境
2)是否有共享数据
3)是否有多条语句对共享数据进行操作
如果上面都是true,那么即存在多线程不安全的问题。为了解决,引入
同步代码块和同步锁
同步代码块格式:
synchronized (锁对象) {
//这里是针对多条语句对共享数据操作的代码
}
Demo:
@Override
public void run() {
while(true) {
//加入锁对象
synchronized (this) {
//加入线程睡眠,模拟延迟
try {
Thread.sleep(100);//延迟时间越长,对CPU原子性操作的抵抗作用越强
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0 ) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + (ticket--) + "张票" );
}
}
}
}
这里加了锁对象,解决了线程安全问题
这里所对象可以是任意一个Object ,当然也可以用this关键字代替 ,如果锁对象是一个静态同步的方法,那么需所对象应该为:方法名.class
private synchronized static void sellTicket() {//这是一个静态同步方法
if(tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在出售第"+(tickets--)+"张票");
}
}
如果使用这个方法作为锁的话,那么我们使用同步代码块的格式是:
synchronized (sellTicket.class){
//这里是针对多条语句对共享数据操作的代码
}
注意:每一个线程只能使用同一把锁(同一个对象)!!!
同步锁(重入锁)
Lock 是一个接口,这个接口中有一个子实现类:ReentrantLock 。我们创建同步锁的对象使用这个类来创建锁对象。在具体的加锁解锁我们使用try{}finally{if(lock != null){lock.unlock() ;}}这个格式来处理。
例子:
@Override
public void run() {
while (true) {
try { //try...finally
lock.lock(); // 获取锁 syncrhonized(obj)
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
} finally {//释放锁
if(lock!=null) {
lock.unlock();
}
}
}
}
死锁
这是解决线程安全问题使用锁后容易出现的问题。死锁的概念是:两个或两个以上的线程,在执行的过程中出现互相等待的情况,就叫做死锁。例子:
public class Test2 {
public static void main(String[] args) {
DieLock dl1 = new DieLock(true) ;
DieLock dl2 = new DieLock(false) ;
dl1.start();
dl2.start();
}
}
/**
* 创建两把锁对象
* @author malaganguo
*
*/
class lockObject {
public static final Object lockobj1 = new Object() ;
public static final Object lockobj2 = new Object() ;
}
class DieLock extends Thread {
private boolean flag ;
public DieLock(boolean flag) {
this.flag = flag ;
}
@Override
public void run() {
if(flag) {
synchronized (lockObject.lockobj1) {
System.out.println("if obj1");
synchronized (lockObject.lockobj2) {
System.out.println("if obj2");
}
}
}else {
synchronized (lockObject.lockobj2) {
System.out.println("else obj2");
synchronized (lockObject.lockobj1) {
System.out.println("else obj1");
}
}
}
}
}
这样运行的话由于死锁的存在,可能会输出的结果组合:
1)if obj1 else obj2
2) if obj2 else obj1
3) 这种可能很小很小 if obj1 ifobj2 else obj2 else obj1
4) 这种可能很小很小 else obj2 else obj1 if obj1 ifobj2
模拟生产消费者模式
两个线程:生产者,消费者
立一个flag,这个flag默认为false,这个false的意思是默认没有产品供消费者使用,所以先执行生产者,生产者执行完毕后,将flag改为true,这个true的意思是产品已经被生产出来了,告诉消费者该消费了。代码如下:
/***
* 生产者消费者模式
* flag为true执行消费状态,为false执行生产状态
* @author malaganguo
*
*/
public class Test3 {
public static void main(String[] args) {
// 针对同一个对象进行操作
Student s = new Student();
System.out.println(s.flag);//可以看到这里执行的结果是false ,即Boolean 的默认值是false,所以首先是消费者等待,唤醒生产者
// 创建线程对象
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
t1.start();
t2.start();
}
}
// 创建一个类
class Student {
private String hasData;
private String setData;
public String getHasData() {
return hasData;
}
public void setHasData(String hasData) {
this.hasData = hasData;
}
public String getSetData() {
return setData;
}
public void setSetData(String setData) {
this.setData = setData;
}
boolean flag;// true:默认有数据 false:默认没有数据
}
// 生产者
class SetThread extends Thread {
private Student s;
public SetThread(Student s) {
this.s = s;
}
// 定义一个变量这个变量可以自加,我们把这个变量的奇偶来输出不同的数据
private int x = 0;
@Override
public void run() {
while (true) {
synchronized (s) {
if (s.flag) {//如果有数据
try {
s.wait();//生产者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (x % 2 == 0) {
s.setHasData("没有数据");
s.setSetData("设置的第一个Data");
} else {
s.setHasData("还是没有数据");
s.setSetData("设置的第二个Data");
}
x++;
s.flag = true; // 生产出数据
s.notify(); // 唤醒消费者
}
}
}
}
class GetThread extends Thread {
private Student s;
public GetThread(Student s) {
this.s = s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
if (!s.flag) {//如果没有数据
try {
s.wait();//消费者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(s.getHasData()); //消费
System.out.println(s.getSetData()); //消费
s.flag = false; //消费完了,没有数据了
s.notify(); //唤醒生产者
}
}
}
}
ThreadGroup线程组
线程组表示一个线程的集合,线程组中还可以有其他的线程组。
package day19.Thread1;
public class ThreadGroupDemo {
public static void main(String[] args) {
Method1();
Method2();
}
/**
* 这个方法得到的线程名称是当前线程的名称
*/
private static void Method2() {
MyThread my = new MyThread() ;
Thread t1 = new Thread(my, "线程1") ;
Thread t2 = new Thread(my, "线程2") ;
ThreadGroup tg1 = t1.getThreadGroup() ;
ThreadGroup tg2 = t2.getThreadGroup() ;
//返回该线程组的名称-----都为main
System.out.println(tg1.getName());
System.out.println(tg2.getName());
//范围当前线程组的名称
System.out.println(Thread.currentThread().getThreadGroup().getName());
}
/**
* 这个方法得到的是构建一个新的线程组
*/
private static void Method1() {
ThreadGroup tg = new ThreadGroup("main——新的线程组");
//创建连个线程对象
MyThread mt = new MyThread() ;
Thread t1 = new Thread(tg, mt, "线程1") ;
Thread t2 = new Thread(tg, mt, "线程2") ;
//获取线程名字
System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
}
}
//创建一个线程类:这个类输出1-100
class MyThread extends Thread {
@Override
public void run() {
for(int x = 1 ; x <= 100 ; x ++ ) {
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
线程池
普通的线程在start()调用完毕,JVM将run()方法调用完毕,线程对象会变成垃圾,等待垃圾回收器回收。
而线程池的特点是:1)节约成本 2)线程执行完成后不会变成垃圾,重新回到线程池中,等待被利用。
Demo:
package day19.Thread1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池
* 1)创建线程池对象,指定线程池的线程数
* 2)往线程池中加入线程
* 3)关闭线程池
*
* Executors是一个工厂类
* public static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池
*
*
* ExecutorService:这是一个接口,可以执行异步任务
* 提交:Future<?> submit(Runnable task)
* <T> Future<T> submit(Callable<T> task)提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future
*
*void shutdown():关闭之前,会提交刚才的任务
*void shutdownNow():试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
*
* @author malaganguo
*/
public class ThreadPool {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3) ;
pool.submit(new MyThread()) ;
pool.submit(new MyThread()) ;
pool.submit(new MyThread()) ;
pool.shutdown();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for(int x = 0 ;x < 100 ;x ++) {
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
Demo2:
package day19.Thread1;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 创建线程池
*
* 1)自定义类实现Callable接口
* 2)创建线程池对象
* 3)调用ExecutorService接口中的submit方法提交任务
* 4)关闭线程池
* @author malaganguo
*
*/
public class ThreadPoolCallable {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ExecutorService pool =Executors.newFixedThreadPool(2) ;
pool.submit(new MyCallable()) ;
pool.submit(new MyCallable()) ;
pool.shutdown();
}
}
class MyCallable implements Callable {
@Override
public Object call() throws Exception {
for(int x = 0 ; x < 100 ; x ++) {
System.out.println(Thread.currentThread().getName()+":"+x);
}
return null;
}
}
Demo3:
package day19.Thread1;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 创建线程池的方法
* 带泛型的
* 可以完成泛型的运算等
* 因为Executors是工厂类,得到的是类对象,需要调用get()方法获取值
*
* @author malaganguo
*
*/
public class ThreadPoolCallableT {
public static void main(String[] args) {
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
//提交任务,返回的是一个类(因为Executors是一个工厂类)
Future<Integer> sum1 = pool.submit(new MyCallable_T(3));
Future<Integer> sum2 = pool.submit(new MyCallable_T(200));
try {
//传值 get()方法用来获取结果
Integer s1 = sum1.get() ;
Integer s2 = sum2.get() ;
//打印结果
System.out.println(s1);
System.out.println(s2);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//带泛型的Callable实现类
class MyCallable_T implements Callable<Integer> {
private int number ;
public MyCallable_T(int number) {
this.number = number ;
}
@Override
public Integer call() throws Exception {
int sum = 0 ;
for(int x = 1 ; x <= number ;x ++ ) {
sum += x ;
}
return sum;
}
}
实时