是正在运行的程序。
是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
是进程中的单个顺序控制流,是一条执行路径。
单线程:一个进程如果只有一条执行路径,则成为单线程程序。
多线程:一个进程如果有多条执行路径,则成为多线程程序。
方式1:继承Thread类
public class test02 {
public static void main(String[] args) {
MyTread myTread=new MyTread();
MyTread myTread1=new MyTread();
myTread.start();
myTread1.start();
}
}
class MyTread extends Thread{
@Override
public void run() {
// super.run();
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程,然后由JVM调用线程的run()方法
Thread类中设置和获取线程名称的方法
获取main()方法所在的线程名称?
public static Thread currentThread():返回对当前正在执行的线程对象的引用
public class test02 {
public static void main(String[] args) {
//无参构造器创建对象
MyTread myTread=new MyTread();
MyTread myTread1=new MyTread();
// //设置线程名
myTread.setName("线程1");
myTread1.setName("线程2");
/****************************/
//带参构造器创建对象
MyTread myTread=new MyTread("线程1");
MyTread myTread1=new MyTread("线程2");
myTread.start();
myTread1.start();
//static Thread currentThread(),返回对当前正在执行的线程对象的引用
System.out.println(Thread.currentThread().getName());
}
}
class MyTread extends Thread{
//无参构造
public MyTread() {
}
//带参构造
public MyTread(String name) {
super(name);
// TODO Auto-generated constructor stub
}
//重写run()方法
@Override
public void run() {
// super.run();
for (int i = 0; i < 100; i++) {
System.out.println(getName()+":"+i);
}
}
}
两种调度模型:
分成调度模型:所以线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些。
Java使用的是抢占式调度模型
多线程程序的执行是有随机性的,因为谁抢到CPU的使用权是不一定的。
Thread类中设置和获取线程优先级的方法
public final int getPriority():返回此线程的优先级
public final void setPriority():更改此线程的优先级
线程默认优先级是5,线程优先级的范围是1-10
线程优先级高仅仅表示获取CPU时间片的几率高,并不是一定要跑在前面。
但是要在次数比较多,或者多次运行之后才能看到想要的结果
//带参构造器创建对象
MyTread myTread=new MyTread("线程1");
MyTread myTread1=new MyTread("线程2");
MyTread myTread2=new MyTread("线程3");
//默认优先级
System.out.println(myTread.getPriority()); //5
System.out.println(myTread1.getPriority()); //5
System.out.println(myTread2.getPriority()); //5
//设置优先级
myTread.setPriority(1);
myTread1.setPriority(5);
myTread2.setPriority(10);
System.out.println(myTread.getPriority()); //1
System.out.println(myTread1.getPriority()); //5
System.out.println(myTread2.getPriority()); //10
myTread.start();
myTread1.start();
myTread2.start();
方法名 | 说明 |
---|---|
static void sleep(long millis) | 使当前正在执行的线程停留(暂停)执行的毫秒数 |
void join() | 等待这个线程死亡 |
void setDaemon() | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出 |
static void sleep(long millis) :使当前正在执行的线程停留(暂停)执行的毫秒数
public class test02 {
public static void main(String[] args) {
ThreadSleep ts1=new ThreadSleep("曹操");
ThreadSleep ts2=new ThreadSleep("刘备");
ThreadSleep ts3=new ThreadSleep("孙权");
ts1.start();
ts2.start();
ts3.start();
}
}
class ThreadSleep extends Thread{
public ThreadSleep() {
}
public ThreadSleep(String name) {
super(name);
}
@Override
//重写run方法
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+":"+i);
try {
Thread.sleep(1000); //每执行一次,停留一秒后再执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
void join():等待这个线程死亡
public class test02 {
public static void main(String[] args) throws InterruptedException {
ThreadJoin ts1=new ThreadJoin ("康熙");
ThreadJoin ts2=new ThreadJoin ("四阿哥");
ThreadJoin ts3=new ThreadJoin ("八阿哥");
ts1.start();
ts1.join(); //等待ts1执行结束后,ts2和ts3才能执行
ts2.start();
ts3.start();
}
}
void setDaemon():将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
ThreadDaemon ts1=new ThreadDaemon ("关羽");
ThreadDaemon ts2=new ThreadDaemon ("张飞");
//设置守护线程
ts1.setDaemon(true);
ts2.setDaemon(true);
ts1.start();
ts2.start();
//设置主线程为“刘备”
Thread.currentThread().setName("刘备");
for(int i=0;i<10;i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
//主线程执行完毕后,守护线程很快执行完毕
实现Runnable接口
相比继承Thread类,实现Runnable接口的好处:
public class test02 {
public static void main(String[] args) throws InterruptedException {
Thread thread1=new Thread(new MyRunnable(),"进程1");
Thread thread2=new Thread(new MyRunnable(),"进程2");
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
锁多条语句操作共享数据,可以使用同步代码块实现。
格式:
synchronized(任意对象){
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁。
好处和弊端:
好处:解决了多线程的数据安全问题
弊端:大概线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
某电影院目前正在上映国产大片,共有100张票,而他有三个窗口卖票,设计一个程序模拟该电影院卖票。
思路:
public class test02 {
public static void main(String[] args) throws InterruptedException {
SellTicket sellTicket=new SellTicket();
Thread t1=new Thread(sellTicket,"窗口1");
Thread t2=new Thread(sellTicket,"窗口2");
Thread t3=new Thread(sellTicket,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
class SellTicket implements Runnable{
private int tickets=100;
private Object obj=new Object();
@Override
public void run() {
while (true) {
//tickets=100;
//t1,t2,t3,
//假设t1抢到了cpu的执行权
synchronized (obj) {
//t1进来后,就会把这段代码锁起来
if (tickets>0) {
try {
Thread.sleep(100);
//t1休息100毫秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//窗口1正在出售第100张票
System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"张票");
tickets--; //tickets=99;
}
}
//t1出来了,这段代码的锁就被释放了
//t1,t2,t3继续抢cpu的执行权然后锁代码
}
}
}
就是把synchronized关键字加到方法上
格式:
修饰符 synchronized 返回值类型 方法名(方法参数){ }
public class test02 {
public static void main(String[] args) throws InterruptedException {
SellTicket sellTicket=new SellTicket();
Thread t1=new Thread(sellTicket,"窗口1");
Thread t2=new Thread(sellTicket,"窗口2");
Thread t3=new Thread(sellTicket,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
class SellTicket implements Runnable{
private int tickets=100;
private Object obj=new Object();
private int x=0;
@Override
public void run() {
while (true) {
if (x%2==0) {
sellTicket();
}else {
sellTicket();
}
x++;
}
}
private synchronized void sellTicket() {
if (tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"张票");
tickets--;
}
}
}
StringBuffer
Vector
HashTable
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里上了锁,在哪里释放了锁。
为了更清晰的表达如何释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比synchronize方法和语句可以获得更广泛的锁定操作。
Lock中提供了获得锁和释放锁的办法
void lock():获得锁
void unlock():释放锁
Lock是接口不能直接实例化,这里采用他的实现类ReentrantLock来实例化
public class test02 {
public static void main(String[] args) throws InterruptedException {
SellTicket sellTicket=new SellTicket();
Thread t1=new Thread(sellTicket,"窗口1");
Thread t2=new Thread(sellTicket,"窗口2");
Thread t3=new Thread(sellTicket,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
class SellTicket implements Runnable{
private int tickets=100;
private Object obj=new Object();
private Lock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if (tickets>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"张票");
tickets--;
}
}finally {
lock.unlock();
}
}
}
}
所谓生产者消费者问题,实际上主要是包含了两类线程:
为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
为了提现生产和消费过程中的等待和唤醒,Java就提供了几个犯法供我们使用,这几个方法在Object类中
方法名 | 说明 |
---|---|
void wait() | 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法 |
void notify() | 唤醒正在等待对象监视器的单个线程 |
void notifyAll() | 唤醒正在等待对象监视器的所有线程 |
public class test02 {
public static void main(String[] args) throws InterruptedException {
//创建奶箱对象,这是共享数据区域
Box box=new Box();
//创建生产者消费者对象
Customer customer=new Customer(box);
Producter producter=new Producter(box);
//创建两个线程对象
Thread t1=new Thread(producter);
Thread t2=new Thread(customer);
//启动线程
t1.start();
t2.start();
}
}
//定义奶箱类
class Box{
//定义一个成员变量,表示第x瓶奶
private int milk;
//定义一个成员变量,表示奶箱的状态
private boolean flag=false;
//提供存储牛奶和获取牛奶的操作
public synchronized void getMilk() throws InterruptedException {
//如果没有牛奶,等待生产
if (!flag) {
wait();
}
//如果有牛奶,取出牛奶
System.out.println("用户拿到第"+this.milk+"瓶奶");
//修改奶箱状态
flag=false;
//唤醒其他等待的线程
notifyAll();
}
public synchronized void setMilk(int milk) throws InterruptedException {
//如果有牛奶,等待消费
if (flag) {
wait();
}
//如果没有牛奶,就生产牛奶
this.milk = milk;
System.out.println("第"+this.milk+"瓶奶放入奶箱");
//生产完毕之后,修改奶箱状态
flag=true;
//唤醒其他等待的线程
notifyAll();
}
}
//定义生产者类
class Producter implements Runnable{
private Box b;
public Producter(Box b) {
this.b=b;
}
//重写run()方法
@Override
public void run() {
while (true) {
try {
b.getMilk();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//定义消费者类
class Customer implements Runnable{
private Box b;
public Customer(Box b) {
this.b=b;
}
//重写run()方法
@Override
public void run() {
for (int i = 1; i <=5; i++) {
try {
b.setMilk(i);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}