-
程序:是为完成特定任务,用某种语言编写的一组指令的集合。简单的说,就是我们写的代码
-
并发:同一个时刻,多个任务交替执行,造成一种貌似同事的错觉,单核cpu实现的多任务就是并发
-
并行:多个任务同事执行,多核cpu可以实现并行
-
小练习:检测本机cpu是多少核:
public class A{
public static void main(String[] args){
Runtime runtime = Runtime.getRuntime();
int cpuNums = runtime.availableProcessors();
System.out.println(cpuNums);
}
}
创建线程的两种方法
(1)继承Thread类,重写run方法
(2)实现Runnable接口,重写run方法
-
小练习:每隔一秒输出“你真好看”,总共输出4次结束
public class A{
public static void main(String[] args){
Cat cat = new Cat();
cat.start();//不能使用run()方法
}
}
//run Thread 类实现了Runnable 接口的run方法
class Cat extends Thread{
@Override
public void run() {//重写run方法
int time = 0;
//每隔一秒输出 你可真好看
while (true) {
System.out.println("你可真好看"+(++time));
//休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(time > 4){
break;
}
}
}
}
-
实现Runnable接口:
(1)java是单继承的,在某些情况下,一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然是不可能的
(2)于是,就有了实现Runnable接口这个方法来创建进程
-
小例子:
基本写法同上,只是在调用的时候有些不同
public class A{
public static void main(String[] args){
Cat cat = new Cat();
Thread thread = new Thread(cat);
thread.start();
}
}
//run Thread 类实现了Runnable 接口的run方法
class Cat implements Runnable{
@Override
public void run() {//重写run方法
int time = 0;
//每隔一秒输出 你可真好看
while (true) {
System.out.println("你可真好看"+(++time));
//休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(time > 4){
break;
}
}
}
}
-
继承Thread vs实现Runable接口
(1)从java设计上来看,两者没有本质区别,因为本质都是靠 start()方法来进行线程
(2)实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制
-
小练习:模拟三个窗口的买票
public class A{
public static void main(String[] args){
// SellTicket sellTicket = new SellTicket();
// SellTicket sellTicket2 = new SellTicket();
// SellTicket sellTicket3 = new SellTicket();
//
// sellTicket.start();
// sellTicket2.start();
// sellTicket3.start();
SellTicket2 sellTicket2 = new SellTicket2();
new Thread(sellTicket2).start();
new Thread(sellTicket2).start();
new Thread(sellTicket2).start();
}
}
class SellTicket extends Thread{
public static int tickets= 100;
@Override
public void run() {
while (true) {
if (tickets < 0) {
System.out.println("售票结束...");
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在售票。。。" + Thread.currentThread().getName() +
"还剩" + (--tickets) + "张票");
}
}
}
class SellTicket2 implements Runnable{
public static int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets <= 0) {
System.out.println("票已售罄...");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("成功买票"+Thread.currentThread().getName()+
"还剩"+(--tickets)+"张票");
}
}
}
运行后会发现,这个程序会有些问题,会发生超卖问题,这个问题设计后面的知识点,在后面的时候再解决
-
线程终止:
添加一个条件让线程停止运行
public class A{
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.start();
Thread.sleep(1000);
t.setLoop(false);
}
}
class T extends Thread{
private int count = 0;
private boolean loop = true;
public void setLoop(boolean loop) {
this.loop = loop;
}
@Override
public void run() {
while (loop){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("运行中..." + (++count));
}
}
}
-
线程常用方法:
yield:线程的礼让,让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入线程所有的任务
public class A{
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.start();
for (int i = 1; i < 20;i++){
Thread.sleep(1000);
System.out.println("主线程给了你"+i+"巴掌");
if(i == 5){
System.out.println("主线程先歇会,让子线程先上");
t.join();
System.out.println("子线程打完,主线程上");
}
}
}
}
class T extends Thread{
@Override
public void run() {
for (int i = 0;i <= 20;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程给了你" + i +"巴掌");
}
}
}
互斥锁
-
基本介绍:
(1)java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
(2)每个对象都对应一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
(3)关键字 synchronized 来与对象的互斥锁联系。的某个对象用synchronized修饰是,表明该对象在任一时刻只能由一个线程访问
(4)同步的局限性:导致程序的执行效率降低
(5)同步方法(非静态的):的锁可以是this,也可以是其他对象(要求是同一个对象)
(6)同步方法(静态的)的锁为当前类本身
-
注意事项和细节
(1)同步方法如果没有使用static修饰:默认锁对象为this
(2)如果方法使用static修饰,默认所对象:当前类.class
释放锁
(1)当前线程的同步方法,同步代码块执行结束
(2)当前线程在同步代码块、同步方法中遇到break、return
(3)当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
(4)当前线程在同步代码块、同步方法中执行了线程对象的wait方法,当前线程暂停,并释放锁
-
以下情况不会释放锁
(1)线程执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁。
(2)线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,不会释放锁
-
练习题:
(1)在main方法中启动两个线程
(2)第一个线程循环随机打印100以内的整数
(3)直到第二个线程从键盘读取了Q命令
public class A{
public static void main(String[] args){
One one = new One();
Two two = new Two(one);
one.start();
two.start();
}
}
class One extends Thread{
private boolean loop = true;
public void setLoop(boolean loop) {
this.loop = loop;
}
@Override
public void run() {
while (loop ){
// 随机输出1 - 100之间的数
System.out.println((int) (Math.random() * 100 + 1));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Two extends Thread{
private One one;
private Scanner myScanner = new Scanner(System.in);
public Two(One one) {
this.one = one;
}
@Override
public void run() {
System.out.println("请输入Q表示退出:");
char q = myScanner.next().toUpperCase().charAt(0);
while (true) {
if (q == 'Q') {
// 以通知的方式使A停止
one.setLoop(false);
System.out.println("b线程退出");
break;
}
}
}
}
(2)两个人从银行里取钱,总金额10000,每次取一千,当余额不足时,提示余额不足
public class A{
public static void main(String[] args){
One one = new One();
Thread thread = new Thread(one);
Thread thread1 = new Thread(one);
thread.start();
thread1.start();
}
}
class One implements Runnable{
private double money = 10000.0;
private boolean loop = true;
@Override
public void run() {
while (loop) {
synchronized (this) {
if (money < 1000) {
System.out.println("余额不足,无法取出");
loop = false;
break;
}
money -= 1000;
System.out.println("您已成功取钱,还剩" + money);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}