1、线程调度有哪些调度方式?
分时调度;
抢占式调度;
2、什么叫主线程?
执行主方法-main方法 的线程;
3、如何利用Thread类来实现多线程?
新线程程序需要放到自己创建的Thread子类的run方法中;
在主程序中,用新线程类的对象调用 start方法 可以跑其中的 run 方法;
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("ZiThread:" + i);
}
}
}
public class test {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for (int i = 0; i < 20; i++) {
System.out.println("main:" + i);
}
}
}
4、调用mt.start() 和 mt.run() 有什么区别?
要是调用 mt.run() 那么就是单线程程序;
main方法在一个栈空间中,mt.run()会在原来的栈中压入;
如果调用mt.start(),那么系统会开辟一个新的栈空间;
CPU有选择栈空间的权利,可以执行任意栈空间的方法;
5、线程的名称是如何规定的?
主线程的名称叫做main;
新线程从Thread-0开始;
6、获取当前系统使用的线程是什么方法?
Thread.currentThread()
7、可以给新线程起名吗?
可以;
8、sleep方法有什么用?如何调用 sleep方法?
(1) sleep方法可以让线程停止一段时间;
(2) sleep方法是类的方法,用Thread类调用即可;
9、如何通过实现Runnable接口的方式启动多线程?
Runnable的实现类(重写run方法);
new 一个 Runnable 实现类对象;
这个实现类对象作为参数传入 Thread()当中构造一个Thread对象;
这种方法第一印象很好;
RunnableImpl r = new RunnableImpl();
Thread t = new Thread(r);
t.start();
10、Runnable接口实现方式创建多线程的有优点是什么?
(1)避免单继承的局限性,Runnable是接口,可实现多个接口,而Thread是类,一个实现类只能继承它一个类,继承它之后就不能继承其他的类;
(2)降低了程序的耦合性,线程的功能是 Runnable实现类的方法的,线程的开启是Thread对象的事情;我一个产品做好了,直接使用在下一个产品中,这样叫降低了耦合性。如果一个类既要重写run方法,它的对象又要调用start方法,这样的话就有很高的耦合性;
11、什么是耦合性?我们应该追求高耦合性还是低耦合性?
耦合性是模块间关联程度的度量;
软件设计中追求高内聚,低耦合;
12、如何通过匿名内部类实现线程的创建?
new 父类/接口(){重写run方法}
Runnable r = new Runnable(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "-" + i);
}
}
};
new Thread(r).start();
13、举例说明说明是线程安全问题?
电影院:
一个窗口卖100张电影票,从编号1卖到100没有问题;
三个窗口卖100张电影票,从编号1卖到100产生问题,例如两个窗口同时卖编号第20的票;
多线程访问了共享的数据,就会出现线程安全问题;
14、多线程卖票如何代码实现?即多线程如何访问共享数据?
让一个线程访问共享数据的时候,无论是否失去了CPU的执行权,让其他线程只能等待;
15、如何使用同步代码块实现卖票线程安全问题?
synchronized(锁对象){访问共享数据的代码}
public class RunnableImpl implements Runnable {
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj){
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->正在卖" + ticket + "张票");
ticket--;
}
}
}
}
}
public class test {
public static void main(String[] args){
RunnableImpl run = new RunnableImpl();
// 创建一个实现类传到三个线程里面
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
16、同步代码块技术实现线程安全的原理是什么?
当某线程抢到CPU执行权的时候,遇到 synchronized 会检查是否有锁对象;
有锁对象就执行同步代码块里面的内容,等到执行完毕就归还锁对象;
没有锁对象就进入阻塞状态,等带锁对象被其他线程归还;
17、同步方法实现卖票多线程安全问题的代码实现是怎样的?
定义一个同步方法,同步方法的特征就是在方法修饰符中多一个 synchronized;
public class RunnableImpl implements Runnable {
private int ticket = 100;
@Override
public void run(){
while(true){
payTicket();
}
}
public synchronized void payTicket(){
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->正在卖" + ticket + "张票");
ticket--;
}
}
}
18、同步方法和同步代码块有什么关系?
同步方法也用到了锁;
同步代码块用到的锁是自己定义的;
同步方法用到的锁实际上是实现类对象 —— new RunnableImpl();
下面的代码片段等价与上面的同步方法;
public /*synchronized*/ void payTicket(){
synchronized (this){
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->正在卖" + ticket + "张票");
ticket--;
}
}
}
19(了解)、如果同步方法是静态同步方法,那么锁对象还能是this吗?不是this的话是什么?
静态同步方法用静态变量(所谓静态的用静态的);
锁对象不能是 this , 因为 this 是创建对象后产生,静态方法优先于对象进行编译;
静态同步方法的锁对象是本类的class属性 —— RunnableImpl.class;
20、如何使用lock锁解决线程安全问题?
使用Lock接口的实现类 ReentrantLock 的对象;
在可能出现线程安全问题的代码前获取锁,lock();
在可能出现线程安全问题的代码后释放锁,unlock();
public class RunnableImpl implements Runnable {
private int ticket = 100;
Lock l = new ReentrantLock(); // 多态
@Override
public void run(){
while(true){
l.lock();
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->正在卖" + ticket + "张票");
ticket--;
}
l.unlock();
}
}
21(了解)、线程有哪些状态?
操作系统的线程的状态图
22、等待与唤醒机制代码如何实现?
同步代码块 + 锁对象.wait() / notify()
public class test2 {
public static void main(String[] args) {
Object obj = new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj){
System.out.println("顾客告诉老板购买包子的数量");
try { // 在同步代码块中写 obj.wait()
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("包子很好吃");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("老板告诉顾客包子做好了");
obj.notify();
}
}
}).start();
}
}
23、实现如下等待与唤醒实例
public class BaoZi {
public String pi;
public String xian;
boolean flag = false;
}
public class BaoZiPu extends Thread{
public int count = 0;
private BaoZi bz;
public BaoZiPu(){
}
public BaoZiPu(BaoZi bz){
this.bz = bz;
}
@Override
public void run() {
while(true){
synchronized (bz){
if(bz.flag){
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
if(count % 2 == 0){
bz.pi = "薄皮";
bz.xian = "素馅";
}
else {
bz.pi = "冰皮";
bz.xian = "肉馅";
}
count++;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("包子铺生产好了" + bz.pi+bz.xian+"的包子");
bz.flag = true;
bz.notify();
}
}
}
}
}
public class ChiHuo extends Thread {
private BaoZi bz;
public ChiHuo() {
}
public ChiHuo(BaoZi bz){
this.bz = bz;
}
@Override
public void run() {
while (true){
synchronized (bz){
if(bz.flag){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("吃货在吃"+bz.pi+bz.xian+"的包子");
bz.flag = false;
bz.notify(); // 吃货吃完了唤醒包子铺做包子
}
else {
try {
bz.wait(); // 没有包子的时候,吃货处于等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class test3 {
public static void main(String[] args) {
BaoZi bz = new BaoZi();
BaoZiPu bzp = new BaoZiPu(bz);
ChiHuo ch = new ChiHuo(bz);
bzp.start();
ch.start();
}
这是一个很好的学习借鉴例题;
在测试类中创建好包子对象,传入其他类中作为对象使用;
24、如果一个线程里面既有wait(),又有notify(),它们会互相通信吗?
wait(),notify() 方法是线程间通讯的方法,并不是线程内通信的方法;
wait() 指的是当前线程释放锁,让出CPU;
notify() 是唤醒其他线程,并不是唤醒本线程;
一个线程里面既有wait(),又有nofity(),这说明这个线程会先notify(),然后再wait();
25、线程池如何使用?
1、创建线程池
ExecutorService es = Executors.newFixedThreadPool(3); // 类名+静态方法名+var;
2、创建Runnable接口的实现类,重写run方法;
3、线程池对象调用submit方法,传入Runnable接口实现类的对象;
26、线程池是什么?作用是什么?
线程池实际上就是一个装很多线程的容器;
线程池的作用是在重复使用线程时,提高时间利用率;