进程:把一个程序在一个数据集上的一次执行称为一个进程。
线程:线程是进程中可独立执行的子任务。一个进程中可以有一个或多个线程。
进程与线程的区别:进程是资源分配单位,线程是调度和执行单位。每一个进程都有自己的主存空间,同一个进程中的线程共享这个主存空间,进程中的所有线程对主存空间都有存取权。
线程的创建和启动
单线程操作:
public class SingleThreadTest {
public static void method1(){
System.out.println("method1");
}
public static void method2(){
System.out.println("method2");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("main方法开始执行");
SingleThreadTest.method1();
SingleThreadTest.method2();
System.out.println("main方法执行结束");
}
}
运行结果:
main方法开始执行
method1
method2
main方法执行结束
方法执行顺序:main()方法→method1()方法→method2()方法
运行main()方法的线程通常称为主线程。主线程都是由JVM来启动的
JVM允许应用程序执行时可以同时运行多个线程,通过java.lang.Thread类来实现。
每个线程对象通过run()方法运行,通过star()方法启动。
创建新的线程
两种方法:
1.继承Thread类并重写run()方法
2.实现Runnable接口的类(一般采用这种方法)
import java.net.URL;
public class SingleThreadTest {
public static void method1(){
System.out.println("method1");
}
public static void method2(){
System.out.println("method2");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
// System.out.println("main方法开始执行");
// SingleThreadTest.method1();
// SingleThreadTest.method2();
// System.out.println("main方法执行结束");
System.out.println("main方法开始执行");
Thread thread1=new MyThread();
MyRunner mr=new MyRunner();
Thread thread2=new Thread(mr);//把它的实例作为参数传入Thread类的构造方法
//启动线程
System.out.println("启动线程1");
thread1.start();
System.out.println("启动线程2");
thread2.start();
System.out.println(thread1.isAlive());
URL url=Thread.currentThread()
.getContextClassLoader()
.getResource("");//使用方法链调用方式获取当前classpath的绝对路径URL表示法
System.out.println(url);
System.out.println("main方法执行结束");
}
}
class MyThread extends Thread{//继承Thread类
public void run(){
//要在线程中执行的代码
for(int i=0;i<100;i++){
System.out.println("MyThread:"+i);
}
}
}
class MyRunner implements Runnable{//实现Runnable接口
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++){
System.out.println("MyRunner:"+i);
}
}
}
运行结果:
main方法开始执行
启动线程1
启动线程2
true
MyThread:0
MyThread:1
MyThread:2
MyThread:3
MyRunner:0
MyThread:4
...
MyThread:99
file:/E:/JavaProject/ThreadProject/bin/
main方法执行结束
Thread类的常用方法
public void star() | 启动线程,执行后不是立刻启动,而是等待jvm调度 |
public static Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
public ClassLoader getContextClassLoader() | 返回该线程的上下文ClassLoader |
public final boolean isAlive() | 测试线程是否活着 |
public Thread.State getState() | 返回该线程的当前状态,true(可运行状态/等待状态)flase(新建状态/中止状态) |
public final String getName() | 返回该线程的名字 |
public final void setName(String name) | 设置该线程的名字 |
public final void setDaemon(boolean on) | 将该线程标记为守护线程或用户线程 |
public final void setPriority(int newPriority) | 更改线程的优先级 |
public static void sleep(long milis)throws InterruptedException | 在指定的毫秒数内让正在执行的进程休眠 |
public void interrupt() | 中断线程 |
public final void join()throws InterruptedException | 等待该线程终止 |
public static void yield() | 暂停当前正在执行的线程对象 |
URL url=Thread.currentThread()
.getContextClassLoader()
.getResource("");//使用方法链调用方式获取当前classpath的绝对路径URL表示法
System.out.println(url);
运行结果:
file:/E:/JavaProject/ThreadProject/bin/
线程分为用户线程和守护线程
守护线程是一种在后台提供通用性支持的线程,在没有其它用户线程运行时,会自动退出。如java垃圾回收线程。
thread.setDaemon(ture) //把用户线程thred变成守护线程
线程的状态及其转换
一个线程创建后,他总是处于六个生命周期的其中一个状态。JDK中用Thread.State枚举了六种状态:
NEW:新建状态
RUNNABLE:可运行状态 正在jvm中执行
BLOCKED:阻塞状态 受堵塞并等待某个监视器锁的进程
WAITING:等待状态 无限期等待另一个进程执行某个操作
TIMED_WAITING:超时等待状态 等待另一个进程执行指定时间的操作的进程
TERMINATED:中止状态 已退出的进程
多线程的调度和优先级
线程的调度:jvm中提供的线程调度器有抢占式模型和分时间片模型
线程的优先级:优先级分为10级。数字越大,优先级越高。MIN_PRIORITY(1)、MAX_PRIORITY(10)、NORMAL_PRIORITY(5),可以通过setPriority()方法设置进程优先级、getPriority()方法获取进程的优先级。
为了提高程序的移植性,不建议手动提高优先级。
public class ThreadPriorityTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t0=new Thread(new A());
Thread t1=new Thread(new A());
System.out.println("进程t0的优先级:"+t0.getPriority());
System.out.println("进程t1的名字:"+t1.getName());
t0.setPriority(10);
t0.start();
t1.start();
}
}
class A implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
//Thread.currentThread()返回当前进程对象的引用
}
}
}
运行结果:
进程t0的优先级:5
进程t1的名字:Thread-1
Thread-0:0
Thread-0:1
...
Thread-0:98
Thread-0:99
Thread-1:0
Thread-1:1
...
Thread-1:99
线程的控制
线程睡眠
用于调整线程的执行顺序
public class ThreadSleepTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t0=new Thread(new SleepRunner());
Thread t1=new Thread(new NormalRunner());
t0.setPriority(10);//最高优先级
t0.start();
t1.start();
}
}
class SleepRunner implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
try{
Thread.sleep(100);//睡100毫秒
}catch(InterruptedException e){
e.printStackTrace();//引发异常的所有方法调用信息
}
for(int i=0;i<100;i++){
System.out.println("SleepRunner:"+i);
}
}
}
class NormalRunner implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++)
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
运行结果:
Thread-1:0
Thread-1:1
...
Thread-1:99
SleepRunner:0
SleepRunner:1
...
SleepRunner:99
线程让步
public class ThreadYieldTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t0=new Thread(new NormalRunner());
Thread t1= new Thread(new YieldRunner());
t0.start();
t1.start();
}
}
class NormalRunner implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++)
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
class YieldRunner implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++){
if(i%10==0){
Thread.yield();//暂停 让步给其它进程
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
运行结果:
...
Thread-1:8
Thread-1:9
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9
Thread-0:10
Thread-0:11
Thread-0:12
Thread-0:13
Thread-0:14
Thread-0:15
Thread-0:16
Thread-0:17
Thread-0:18
Thread-0:19
Thread-0:20
Thread-0:21
Thread-1:10
...
线程加入
当线程需要接力来完成某项人物时,join()方法可以可以使两个交叉运行的线程变成顺序执行。
public class ThreadJoinTste {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Thread t=new Thread(new JoinRunner());
t.start();
for (int i=0;i<=50;i++){
System.out.println("main:"+i);
try{
if(i==30){ //在这之前t和main交替运行,当i=30时、只有t进程运行,t运行结束后、main把未完成的运行
t.join();
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
class JoinRunner implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++){
System.out.println("JoinRunner:"+i);
}
// try{
// Thread.sleep(10);//先让main线程先运行
// }catch(InterruptedException e){
// e.printStackTrace();
// }
}
}
多线程的同步
如果一个程序中定义多个线程,同时对程序中的变量进行访问,赋值修改等,那么这么线程会影响对方的运行。可以对访问进行同步解决。
使用关键词synchronized来同步方法(使用synchronized修饰方法)或同步代码块(synchronized(this){ 同步代码块 },这种办法效率较高)、或者使用对象锁将代码块锁住。
public class TrickOfficeTest {
public static void main(String[] args){
//五个卖票处 一共1000张票
SellTrickRunner sell=new SellTrickRunner();
Thread t1=new Thread(sell);
t1.setName("售票处1");
t1.start();
Thread t2=new Thread(sell);
t2.setName("售票处2");
t2.start();
Thread t3=new Thread(sell);
t3.setName("售票处3");
t3.start();
Thread t4=new Thread(sell);
t4.setName("售票处4");
t4.start();
Thread t5=new Thread(sell);
t5.setName("售票处5");
t5.start();
}
}
class SellTrickRunner implements Runnable{
private int trickNumber=0;
@Override
public void run() {
// TODO Auto-generated method stub
boolean flag=true;
while(flag){
flag=sell();
}
}
public boolean sell(){ //同步售票方法
boolean flag=true;
synchronized(this){//JVM会保证线程执行synchronized代码块时不会被其它线程打断
if(trickNumber<1000){
trickNumber=trickNumber+1;//两个线程同时对共享数据操作,就会出错。
System.out.println(Thread.currentThread().getName()+"卖出第"+trickNumber+"张票。");
}else{
flag=false;
}
try{
Thread.sleep(15);//睡眠5秒 增大出错几率
}catch(InterruptedException e){
e.printStackTrace();
}
return flag;
}
}
}
private Lock lock=new ReentrantLock();//创建Lock实例
lock.lock();
if(trickNumber<1000){
trickNumber=trickNumber+1;//两个线程同时对共享数据操作,就会出错。
System.out.println(Thread.currentThread().getName()+"卖出第"+trickNumber+"张票。");
}else{
flag=false;
}
lock.unlock();
运行结果:
售票处1卖出第1张票。
售票处2卖出第2张票。
售票处3卖出第3张票。
...
售票处4卖出第998张票。
售票处1卖出第999张票。
售票处3卖出第1000张票。
死锁
转自:https://www.cnblogs.com/mudao/p/5867107.html
/*创建两个字符串a和b,
* 再创建两个线程A和B,
* 让每个线程都用synchronized锁住字符串(A先锁a,再去锁b;B先锁b,再锁a),
* 如果A锁住a,B锁住b,A就没办法锁住b,B也没办法锁住a,
* 这时就陷入了死锁。*/
public class DeadLock {
public static String obj1 = "obj1";
public static String obj2 = "obj2";
public static void main(String[] args){
Thread a = new Thread(new Lock1());
Thread b = new Thread(new Lock2());
a.start();
b.start(); //如果只让a运行,就可以无限循环锁住
}
}
class Lock1 implements Runnable{
@Override
public void run(){
try{
System.out.println("Lock1 running");
while(true){
synchronized(DeadLock.obj1){
System.out.println("Lock1 lock obj1");
Thread.sleep(3000);//获取obj1后先等一会儿,让Lock2有足够的时间锁住obj2
synchronized(DeadLock.obj2){
System.out.println("Lock1 lock obj2");//B把obj2锁住了 A不能锁住 所以不能运行
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
class Lock2 implements Runnable{
@Override
public void run(){
try{
System.out.println("Lock2 running");
while(true){
synchronized(DeadLock.obj2){
System.out.println("Lock2 lock obj2");
Thread.sleep(3000);
synchronized(DeadLock.obj1){
System.out.println("Lock2 lock obj1");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
Lock1 running
Lock2 running
Lock1 lock obj1
Lock2 lock obj2
解决死锁问题:加大锁的颗粒度,把各个资源的操作代码放在一个同步块内
线程交互
java.lang.Object类提供了wait、notify、notifyAll方法用于同步机制,但只能在synchronized方法、synchronized代码块和Lock里面用,否则会报错。
wait方法调用、当前线程将被中断运行,并且放弃该对象的锁。
notify方法调用,会唤醒并运行在此对象等待池中的某个线程。
notifyAll方法会唤醒所有等待这个对象的线程,使之称为可运行的线程
/*生产者消费者问题*/
public class ProductTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Repertory repertory=new Repertory();
new Thread(new Producer(repertory)).start();
new Thread(new Consumer(repertory)).start();
}
}
class Repertory{//仓库
private int product=0;//默认有0个商品
public synchronized void addProduct(){ //生产者把产品放入仓库
if(this.product>=5){
try{
this.wait();//满了
}catch(InterruptedException e){
e.printStackTrace();
}
}else{
product++;
System.out.println("生产者生产第"+product+"件商品");
this.notifyAll();//通知等候区的消费者取商品
}
}
public synchronized void getProduct(){//消费者从仓库拿东西
if((this.product)<=0){
try{this.wait();//缺货
}catch(InterruptedException e){
e.printStackTrace();
}
}else{
System.out.println("消费者取走了第"+product+"件商品");
this.product--;
this.notifyAll();
}
}
}
class Producer implements Runnable{
private Repertory repertory;
public Producer(Repertory repertory) {
this.repertory=repertory;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("生产者开始生产商品");
while(true){
try{
Thread.sleep((int)(Math.random()*10)*100);//Math.random()返回带正号的double值 [0.0,1.0)
}catch(InterruptedException e){
e.printStackTrace();
}
repertory.addProduct();
}
}
}
class Consumer implements Runnable{
private Repertory repertory;
public Consumer(Repertory repertory) {
this.repertory=repertory;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("消费者开始取走商品");
while(true){
try{
Thread.sleep((int)(Math.random()*10)*100);//Math.random()返回带正号的double值 [0.0,1.0)
}catch(InterruptedException e){
e.printStackTrace();
}
repertory.getProduct();
}
}
}
运行结果:
生产者开始生产商品
消费者开始取走商品
生产者生产第1件商品
消费者取走了第1件商品
生产者生产第1件商品
消费者取走了第1件商品
生产者生产第1件商品
消费者取走了第1件商品
生产者生产第1件商品
生产者生产第2件商品
消费者取走了第2件商品
生产者生产第2件商品
生产者生产第3件商品
生产者生产第4件商品
...
用Timer类调度任务
java.util.Timer类用于执行定时任务
java.util.TimerTask用于创建定时任务
创建定时任务:继承TimerTask类并重写run()方法,将任务内容写在方法体内
执行定时任务:Timer类常用方法:
public void schedule(TimerTask task,long delay,long period):重复地以固定的延迟时间去执行一个任务。
public void scheduleAtFixedRate(TimerTask task,long delay,long period):重复地以固定的频率去执行一个任务。
public void cancle();终止此计时器
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Timer timer=new Timer();//创建一个计时器
// timer.schedule(new MyTask(), 0,1000);//立刻执行 每隔 1000毫秒 = 1s 执行一次
timer.scheduleAtFixedRate(new MyTask(), 0, 1000);
try{
Thread.sleep(10000);//主线程睡眠10s
}catch(InterruptedException e){
e.printStackTrace();
}
timer.cancel();
}
}
class MyTask extends TimerTask{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("起床");
}
}
运行结果:
起床
起床
起床
起床
起床
起床
起床
起床
起床
起床